Decompiled source of MonsterHotkeys v2.3.1
com.github.zehsteam.MonsterHotkeys.dll
Decompiled 2 weeks 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 System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using ControlValley; using DunGen; using DunGen.Graph; using GameNetcodeStuff; using HarmonyLib; using LethalCompanyInputUtils.Api; using LethalCompanyTestMod; using LethalConfig; using LethalConfig.ConfigItems; using LethalConfig.ConfigItems.Options; using Microsoft.CodeAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; using UnityEngine.InputSystem; using UnityEngine.UI; using com.github.zehsteam.MonsterHotkeys.Data; using com.github.zehsteam.MonsterHotkeys.Dependencies; using com.github.zehsteam.MonsterHotkeys.Dependencies.CrowdControl; using com.github.zehsteam.MonsterHotkeys.Dependencies.CrowdControl.Patches; using com.github.zehsteam.MonsterHotkeys.Enums; using com.github.zehsteam.MonsterHotkeys.Extensions; using com.github.zehsteam.MonsterHotkeys.Helpers; using com.github.zehsteam.MonsterHotkeys.MonoBehaviours; using com.github.zehsteam.MonsterHotkeys.NetcodePatcher; using com.github.zehsteam.MonsterHotkeys.Patches; using com.github.zehsteam.MonsterHotkeys.Twitch; using com.github.zehsteam.MonsterHotkeys.Twitch.Commands; using com.github.zehsteam.TwitchChatAPI; using com.github.zehsteam.TwitchChatAPI.Objects; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("com.github.zehsteam.MonsterHotkeys")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("Spawn Monsters and Monster Plushies using Hotkeys or from Twitch Subs, Cheers, and Raids. Supports Modded Monsters. Highly Configurable. (Twitch and CrowdControl Integration)")] [assembly: AssemblyFileVersion("2.3.1.0")] [assembly: AssemblyInformationalVersion("2.3.1+51a65cde01ebbcc64cf28c5bff711b944be0c129")] [assembly: AssemblyProduct("MonsterHotkeys")] [assembly: AssemblyTitle("com.github.zehsteam.MonsterHotkeys")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.3.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] [module: NetcodePatchedAssembly] internal class <Module> { static <Module>() { } } 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.MonsterHotkeys { public class ConfigManager { public ConfigEntry<bool> ExtendedLogging { get; private set; } public ConfigEntry<bool> Enemy_OnlyHostSpawnEnemies { get; private set; } public ConfigEntry<bool> Enemy_MuteSpawnSFX { get; private set; } public ConfigEntry<bool> Enemy_SpawnStunned { get; private set; } public ConfigEntry<float> Enemy_StunDuration { get; private set; } public SyncedConfigEntry<int> Plushie_SpawnCount { get; private set; } public SyncedConfigEntry<float> Plushie_DespawnDuration { get; private set; } public ConfigEntry<float> Plushie_SFXVolume { get; private set; } public ConfigEntry<bool> Plushie_PlaySFXOnCollision { get; private set; } public SyncedConfigEntry<bool> Plushie_AttractDogs { get; private set; } public SyncedConfigEntry<float> Plushie_Scale { get; private set; } public ConfigEntry<bool> Message_ShowMessages { get; private set; } public ConfigEntry<bool> Message_ShowSpawnEnemyMessages { get; private set; } public ConfigEntry<bool> Message_ShowLocalSpawnEnemyMessages { get; private set; } public ConfigEntry<bool> Message_ShowDetailedTwitchEventSpawnEnemyMessages { get; private set; } public ConfigEntry<float> Message_Duration { get; private set; } public ConfigEntry<int> Message_FontSize { get; private set; } public ConfigEntry<int> Message_BackgroundTransparency { get; private set; } public ConfigEntry<bool> CrowdControl_Enabled { get; private set; } public ConfigEntry<bool> CrowdControl_RewardSpawnPoints { get; private set; } public ConfigEntry<bool> TwitchIntegration_Enabled { get; private set; } public ConfigEntry<bool> TwitchIntegration_SpawnAllOfTheSameEnemy { get; private set; } public ConfigEntry<bool> TwitchSubEvent_Enabled { get; private set; } public ConfigEntry<int> TwitchSubEvent_EnemiesPerSub { get; private set; } public ConfigEntry<int> TwitchSubEvent_Tier2EnemyMultiplier { get; private set; } public ConfigEntry<int> TwitchSubEvent_Tier3EnemyMultiplier { get; private set; } public ConfigEntry<bool> TwitchCheerEvent_Enabled { get; private set; } public ConfigEntry<int> TwitchCheerEvent_AmountToSpawnEnemy { get; private set; } public ConfigEntry<int> TwitchCheerEvent_AmountToSpawnPlushies { get; private set; } public ConfigEntry<bool> TwitchRaidEvent_Enabled { get; private set; } public ConfigEntry<int> TwitchRaidEvent_ViewersPerEnemy { get; private set; } public ConfigEntry<int> TwitchRaidEvent_MaxSpawnCount { get; private set; } public ConfigEntry<bool> SpawnPoints_Enabled { get; private set; } public ConfigEntry<int> SpawnPoints_MaxSpawnsPerDay { get; private set; } public ConfigEntry<int> SpawnPoints_RewardPointsPerDeath { get; private set; } public ConfigEntry<int> SpawnPoints_RewardPointsPerCrewmateDeath { get; private set; } public ConfigEntry<bool> EnemyNametag_Enabled { get; private set; } public ConfigEntry<bool> EnemyNametag_ShowPlatform { get; private set; } public ConfigEntry<float> EnemyNametag_ScaleMultiplier { get; private set; } public ConfigEntry<int> EnemyNametag_BackgroundTransparency { get; private set; } public ConfigManager() { BindConfigs(); MigrateOldConfigSettings(); } private void BindConfigs() { ConfigHelper.SkipAutoGen(); ExtendedLogging = ConfigHelper.Bind("General", "ExtendedLogging", defaultValue: false, "Enable extended logging."); Enemy_OnlyHostSpawnEnemies = ConfigHelper.Bind("Enemy", "OnlyHostSpawnEnemies", defaultValue: false, "If enabled, only the host can spawn enemies."); Enemy_MuteSpawnSFX = ConfigHelper.Bind("Enemy", "MuteSpawnSFX", defaultValue: false, "If enabled, the enemy spawn sfx will not play."); Enemy_SpawnStunned = ConfigHelper.Bind("Enemy", "SpawnStunned", defaultValue: false, "If enabled, spawned enemies will be stunned for StunDuration seconds."); Enemy_StunDuration = ConfigHelper.Bind("Enemy", "StunDuration", 6f, "The duration enemies will be stunned for in seconds."); Enemy_MuteSpawnSFX.SettingChanged += AudioPlayerManager.OnSettingsChanged; Plushie_SpawnCount = ConfigHelper.BindSynced("Plushie", "SpawnCount", 30, "The amount of plushies to spawn.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100)); Plushie_DespawnDuration = ConfigHelper.BindSynced("Plushie", "DespawnDuration", 15f, "The duration in seconds until a plushie gets despawned."); Plushie_SFXVolume = ConfigHelper.Bind("Plushie", "SFXVolume", 25f, "The volume of the plushie's squeak sound effect.", requiresRestart: false, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)); Plushie_PlaySFXOnCollision = ConfigHelper.Bind("Plushie", "PlaySFXOnCollision", defaultValue: true, "If enabled, the plushies will play a sound effect when colliding with the environment."); Plushie_AttractDogs = ConfigHelper.BindSynced("Plushie", "AttractDogs", defaultValue: true, "If enabled, the plushies will attract dogs when making noise."); Plushie_Scale = ConfigHelper.BindSynced("Plushie", "Scale", 4f, "The size of the plushies."); Message_ShowMessages = ConfigHelper.Bind("Message", "ShowMessages", defaultValue: true, "If enabled, will show messages in the bottom right."); Message_ShowSpawnEnemyMessages = ConfigHelper.Bind("Message", "ShowSpawnEnemyMessages", defaultValue: true, "If enabled, will show a message when someone else spawns an enemy."); Message_ShowLocalSpawnEnemyMessages = ConfigHelper.Bind("Message", "ShowLocalSpawnEnemyMessages", defaultValue: true, "If enabled, will show a message when you spawn an enemy."); Message_ShowDetailedTwitchEventSpawnEnemyMessages = ConfigHelper.Bind("Message", "ShowDetailedTwitchEventSpawnEnemyMessages", defaultValue: true, "If enabled, will show a detailed Twitch event spawn enemy messages."); Message_Duration = ConfigHelper.Bind("Message", "Duration", 8f, "The duration of a message in seconds."); Message_FontSize = ConfigHelper.Bind("Message", "FontSize", 25, "The font size of the messages."); Message_BackgroundTransparency = ConfigHelper.Bind("Message", "BackgroundTransparency", 192, "The transparency of the message background.", requiresRestart: false, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 255)); Message_FontSize.SettingChanged += MessageItem.OnSettingsChanged; Message_BackgroundTransparency.SettingChanged += MessageItem.OnSettingsChanged; CrowdControl_Enabled = ConfigHelper.Bind("Crowd Control Integration", "Enabled", defaultValue: true, "If enabled, Crowd Control will be able to spawn enemies anywhere and spawned enemies will have a nametag with the name of the viewer that spawned that enemy."); CrowdControl_RewardSpawnPoints = ConfigHelper.Bind("Crowd Control Integration", "RewardSpawnPoints", defaultValue: true, "If you die to an enemy spawned by a viewer, they will gain a spawn point to spawn a free enemy by writing !spawn in your Twitch chat. (Requries Spawn Points to be enabled, Twitch Integration to be enabled, and the TwitchChatAPI mod)"); TwitchIntegration_Enabled = ConfigHelper.Bind("Twitch Integration", "Enabled", defaultValue: true, "If enabled, Twitch integration will be enabled to spawn enemies from Subs, Cheers, and Raids. (Requires the TwitchChatAPI mod)"); TwitchIntegration_SpawnAllOfTheSameEnemy = ConfigHelper.Bind("Twitch Integration", "SpawnAllOfTheSameEnemy", defaultValue: false, "If enabled, when spawning multiple enemies from a single event, all enemies spawned will be of the same type."); TwitchSubEvent_Enabled = ConfigHelper.Bind("Twitch Sub Event", "Enabled", defaultValue: true, "If enabled, Twitch subs will be able to spawn enemies. (Requires Twitch Integration to be enabled and the TwitchChatAPI mod)"); TwitchSubEvent_EnemiesPerSub = ConfigHelper.Bind("Twitch Sub Event", "EnemiesPerSub", 1, "The amount of enemies that will spawn per sub."); TwitchSubEvent_Tier2EnemyMultiplier = ConfigHelper.Bind("Twitch Sub Event", "Tier2EnemyMultiplier", 3, "The amount to multiply the enemy spawn count for tier 2 subs."); TwitchSubEvent_Tier3EnemyMultiplier = ConfigHelper.Bind("Twitch Sub Event", "Tier3EnemyMultiplier", 6, "The amount to multiply the enemy spawn count for tier 3 subs."); TwitchCheerEvent_Enabled = ConfigHelper.Bind("Twitch Cheer Event", "Enabled", defaultValue: true, "If enabled, Twitch cheers will be able to spawn enemies. (Requires Twitch Integration to be enabled and the TwitchChatAPI mod)"); TwitchCheerEvent_AmountToSpawnEnemy = ConfigHelper.Bind("Twitch Cheer Event", "AmountToSpawnEnemy", 350, "The amount of bits to spawn an enemy."); TwitchCheerEvent_AmountToSpawnPlushies = ConfigHelper.Bind("Twitch Cheer Event", "AmountToSpawnPlushies", 100, "The amount of bits to spawn plushies that create noise and block your view."); TwitchRaidEvent_Enabled = ConfigHelper.Bind("Twitch Raid Event", "Enabled", defaultValue: true, "If enabled, Twitch raids will be able to spawn enemies. (Requires Twitch Integration to be enabled and the TwitchChatAPI mod)"); TwitchRaidEvent_ViewersPerEnemy = ConfigHelper.Bind("Twitch Raid Event", "ViewersPerEnemy", 5, "The amount of viewers for each enemy spawn."); TwitchRaidEvent_MaxSpawnCount = ConfigHelper.Bind("Twitch Raid Event", "MaxSpawnCount", 20, "The max amount of enemies that can spawn."); SpawnPoints_Enabled = ConfigHelper.Bind("Spawn Points", "Enabled", defaultValue: true, "If you die to an enemy spawned by a viewer, they will gain a spawn point to spawn a free enemy by writing !spawn in your Twitch chat. (Requires Twitch Integration to be enabled and the TwitchChatAPI mod)"); SpawnPoints_MaxSpawnsPerDay = ConfigHelper.Bind("Spawn Points", "MaxSpawnsPerDay", 100, "The max amount of spawn points that can be redeemed per in-game day."); SpawnPoints_RewardPointsPerDeath = ConfigHelper.Bind("Spawn Points", "RewardPointsPerDeath", 1, "The amount of spawn points a viewer will receive from your death."); SpawnPoints_RewardPointsPerCrewmateDeath = ConfigHelper.Bind("Spawn Points", "RewardPointsPerCrewmateDeath", 0, "The amount of spawn points a viewer will receive from a crewmate's death."); EnemyNametag_Enabled = ConfigHelper.Bind("Enemy Nametag", "Enabled", defaultValue: true, "If enabled, enemies will spawn with a nametag of the viewer that spawned that enemy."); EnemyNametag_ShowPlatform = ConfigHelper.Bind("Enemy Nametag", "ShowPlatform", defaultValue: true, "If enabled, nametags will show which platform the enemy was spawned from."); EnemyNametag_ScaleMultiplier = ConfigHelper.Bind("Enemy Nametag", "ScaleMultiplier", 1f, "The scale multiplier for enemy nametags.", requiresRestart: false, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 5f)); EnemyNametag_BackgroundTransparency = ConfigHelper.Bind("Enemy Nametag", "BackgroundTransparency", 192, "The transparency of the nametag background.", requiresRestart: false, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 255)); EnemyNametag_ScaleMultiplier.SettingChanged += EnemyNametag.OnSettingsChanged; EnemyNametag_ShowPlatform.SettingChanged += EnemyNametag.OnSettingsChanged; EnemyNametag_BackgroundTransparency.SettingChanged += EnemyNametag.OnSettingsChanged; } private void MigrateOldConfigSettings() { foreach (KeyValuePair<ConfigDefinition, string> orphanedConfigEntry in ConfigHelper.GetOrphanedConfigEntries()) { MigrateOldConfigSetting(orphanedConfigEntry.Key.Section, orphanedConfigEntry.Key.Key, orphanedConfigEntry.Value); } } private void MigrateOldConfigSetting(string section, string key, string value) { StringComparison comparisonType = StringComparison.OrdinalIgnoreCase; if (section.Equals("General Settings", comparisonType) && key.Equals("ExtendedLogging", comparisonType)) { ConfigHelper.SetConfigEntryValue<bool>(ExtendedLogging, value); return; } if (section.Equals("Monster Settings", comparisonType)) { if (key.Equals("OnlyHostSpawnMonsters", comparisonType)) { ConfigHelper.SetConfigEntryValue<bool>(Enemy_OnlyHostSpawnEnemies, value); return; } if (key.Equals("MuteSpawnSFX", comparisonType)) { ConfigHelper.SetConfigEntryValue<bool>(Enemy_MuteSpawnSFX, value); return; } if (key.Equals("SpawnStunned", comparisonType)) { ConfigHelper.SetConfigEntryValue<bool>(Enemy_SpawnStunned, value); return; } if (key.Equals("StunDuration", comparisonType)) { ConfigHelper.SetConfigEntryValue<float>(Enemy_StunDuration, value); return; } } if (section.Equals("Plushie Settings", comparisonType)) { if (key.Equals("SpawnCount", comparisonType)) { ConfigHelper.SetConfigEntryValue(Plushie_SpawnCount, value); return; } if (key.Equals("DespawnDuration", comparisonType)) { ConfigHelper.SetConfigEntryValue(Plushie_DespawnDuration, value); return; } if (key.Equals("SFXVolume", comparisonType)) { ConfigHelper.SetConfigEntryValue<float>(Plushie_SFXVolume, value); return; } if (key.Equals("PlaySFXOnCollision", comparisonType)) { ConfigHelper.SetConfigEntryValue<bool>(Plushie_PlaySFXOnCollision, value); return; } if (key.Equals("AttractDogs", comparisonType)) { ConfigHelper.SetConfigEntryValue(Plushie_AttractDogs, value); return; } if (key.Equals("Scale", comparisonType)) { ConfigHelper.SetConfigEntryValue(Plushie_Scale, value); return; } } if (section.Equals("Message Settings", comparisonType)) { if (key.Equals("ShowMessages", comparisonType)) { ConfigHelper.SetConfigEntryValue<bool>(Message_ShowMessages, value); return; } if (key.Equals("ShowSpawnEnemyMessages", comparisonType)) { ConfigHelper.SetConfigEntryValue<bool>(Message_ShowSpawnEnemyMessages, value); return; } if (key.Equals("ShowLocalSpawnEnemyMessages", comparisonType)) { ConfigHelper.SetConfigEntryValue<bool>(Message_ShowLocalSpawnEnemyMessages, value); return; } if (key.Equals("ShowDetailedTwitchEventSpawnEnemyMessages", comparisonType)) { ConfigHelper.SetConfigEntryValue<bool>(Message_ShowDetailedTwitchEventSpawnEnemyMessages, value); return; } if (key.Equals("Duration", comparisonType)) { ConfigHelper.SetConfigEntryValue<float>(Message_Duration, value); return; } if (key.Equals("FontSize", comparisonType)) { ConfigHelper.SetConfigEntryValue<int>(Message_FontSize, value); return; } if (key.Equals("BackgroundTransparency", comparisonType)) { ConfigHelper.SetConfigEntryValue<int>(Message_BackgroundTransparency, value); return; } } if (section.Equals("Crowd Control Integration Settings", comparisonType) && key.Equals("Enabled", comparisonType)) { ConfigHelper.SetConfigEntryValue<bool>(CrowdControl_Enabled, value); return; } if (section.Equals("Twitch Integration Settings", comparisonType) && key.Equals("Enabled", comparisonType)) { ConfigHelper.SetConfigEntryValue<bool>(TwitchIntegration_Enabled, value); return; } if (section.Equals("Twitch Integration", comparisonType)) { if (key.Equals("SpawnPointsEnabled", comparisonType)) { ConfigHelper.SetConfigEntryValue<bool>(SpawnPoints_Enabled, value); return; } if (key.Equals("SpawnPointsPerDeath", comparisonType)) { ConfigHelper.SetConfigEntryValue<int>(SpawnPoints_RewardPointsPerDeath, value); return; } } if (section.Equals("Twitch Subs Event Settings", comparisonType)) { if (key.Equals("Enabled", comparisonType)) { ConfigHelper.SetConfigEntryValue<bool>(TwitchSubEvent_Enabled, value); return; } if (key.Equals("SpawnAllOfTheSameEnemy", comparisonType)) { ConfigHelper.SetConfigEntryValue<bool>(TwitchIntegration_SpawnAllOfTheSameEnemy, value); return; } } if (section.Equals("Twitch Bits Event Settings", comparisonType)) { if (key.Equals("Enabled", comparisonType)) { ConfigHelper.SetConfigEntryValue<bool>(TwitchCheerEvent_Enabled, value); return; } if (key.Equals("MinAmountToSpawnMonster", comparisonType)) { ConfigHelper.SetConfigEntryValue<int>(TwitchCheerEvent_AmountToSpawnEnemy, value); return; } if (key.Equals("MinAmountToSpawnPlushies", comparisonType)) { ConfigHelper.SetConfigEntryValue<int>(TwitchCheerEvent_AmountToSpawnPlushies, value); return; } } if (section.Equals("Enemy Nametag Settings", comparisonType)) { if (key.Equals("Enabled", comparisonType)) { ConfigHelper.SetConfigEntryValue<bool>(EnemyNametag_Enabled, value); } else if (key.Equals("ShowPlatform", comparisonType)) { ConfigHelper.SetConfigEntryValue<bool>(EnemyNametag_ShowPlatform, value); } else if (key.Equals("ScaleMultiplier", comparisonType)) { ConfigHelper.SetConfigEntryValue<float>(EnemyNametag_ScaleMultiplier, value); } else if (key.Equals("BackgroundTransparency", comparisonType)) { ConfigHelper.SetConfigEntryValue<int>(EnemyNametag_BackgroundTransparency, value); } } } } internal static class Content { public static GameObject NetworkHandlerPrefab { get; private set; } public static GameObject MessageCanvasPrefab { get; private set; } public static EnemyDataList EnemyDataList { get; private set; } public static void Load() { LoadAssetsFromAssetBundle(); } private static void LoadAssetsFromAssetBundle() { AssetBundle val = LoadAssetBundle("monsterhotkeys_assets"); if (!((Object)(object)val == (Object)null)) { NetworkHandlerPrefab = LoadAssetFromAssetBundle<GameObject>("NetworkHandler", val); MessageCanvasPrefab = LoadAssetFromAssetBundle<GameObject>("MonsterHotkeysCanvas", val); EnemyDataList = LoadAssetFromAssetBundle<EnemyDataList>("EnemyDataList", val); Plugin.Logger.LogInfo((object)"Successfully loaded assets from AssetBundle!"); } } private static AssetBundle LoadAssetBundle(string fileName) { try { string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)Plugin.Instance).Info.Location); string text = Path.Combine(directoryName, fileName); return AssetBundle.LoadFromFile(text); } catch (Exception arg) { Plugin.Logger.LogError((object)$"Failed to load AssetBundle \"{fileName}\". {arg}"); } return null; } private static T LoadAssetFromAssetBundle<T>(string name, AssetBundle assetBundle) where T : Object { if (string.IsNullOrWhiteSpace(name)) { Plugin.Logger.LogError((object)("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) { Plugin.Logger.LogError((object)("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) { Plugin.Logger.LogError((object)("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; } } internal class HotkeyInputClass : LcInputActions { [InputAction(/*Could not decode attribute arguments.*/)] public InputAction MonsterPrefixKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction PlushiePrefixKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnRandomKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnBaboonHawkKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnBarberKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnBlobKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnBrackenKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnBunkerSpiderKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnButlerKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnCoilHeadKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnEarthLeviathanKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnEyelessDogKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnForestGiantKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnGhostGirlKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnHoardingBugKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnJesterKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnKidnapperFoxKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnManeaterKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnManticoilKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnMaskedKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnNutcrackerKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnOldBirdKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnSnareFleaKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnSporeLizardKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnThumperKey { get; set; } [InputAction(/*Could not decode attribute arguments.*/)] public InputAction SpawnTulipSnakeKey { get; set; } } public static class HotkeyListener { public static bool DisableHotkeys; public static bool IsMonsterPrefixKeyPressed => Plugin.InputActionsInstance.MonsterPrefixKey.IsPressed(); public static bool IsPlushiePrefixKeyPressed => Plugin.InputActionsInstance.PlushiePrefixKey.IsPressed(); internal static void SetupKeybindCallbacks() { Plugin.InputActionsInstance.SpawnRandomKey.performed += OnSpawnRandomKeyPressed; Plugin.InputActionsInstance.SpawnBaboonHawkKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnBarberKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnBlobKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnBrackenKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnBunkerSpiderKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnButlerKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnCoilHeadKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnEarthLeviathanKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnEyelessDogKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnForestGiantKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnGhostGirlKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnHoardingBugKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnJesterKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnKidnapperFoxKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnManeaterKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnManticoilKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnMaskedKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnNutcrackerKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnOldBirdKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnSnareFleaKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnSporeLizardKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnThumperKey.performed += OnSpawnKeyPressed; Plugin.InputActionsInstance.SpawnTulipSnakeKey.performed += OnSpawnKeyPressed; Plugin.Logger.LogInfo((object)"Setup keybind callbacks."); } private static void OnSpawnKeyPressed(CallbackContext context) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) if (!((CallbackContext)(ref context)).performed) { return; } string name = ((CallbackContext)(ref context)).action.name; Plugin.Instance.LogInfoExtended(name + " key pressed."); if (!Utils.CanPerformHotkeys()) { return; } if (DisableHotkeys && (IsMonsterPrefixKeyPressed || IsPlushiePrefixKeyPressed)) { MessageCanvas.Instance.ShowMessage_LocalClient("Hotkeys have been disabled by another mod", Color.red); Plugin.Logger.LogInfo((object)"Hotkeys have been disabled by another mod."); return; } string text = name.Replace("Spawn", "").Replace("Key", ""); switch (text) { case "BaboonHawk": text = "Baboon hawk"; break; case "BunkerSpider": text = "Bunker Spider"; break; case "Bracken": text = "Flowerman"; break; case "CoilHead": text = "Spring"; break; case "EarthLeviathan": text = "Earth Leviathan"; break; case "EyelessDog": text = "MouthDog"; break; case "GhostGirl": text = "Girl"; break; case "HoardingBug": text = "Hoarding bug"; break; case "OldBird": text = "RadMech"; break; case "SnareFlea": text = "Centipede"; break; case "SporeLizard": text = "Puffer"; break; case "Thumper": text = "Crawler"; break; case "TulipSnake": text = "Tulip Snake"; break; case "KidnapperFox": text = "Bush Wolf"; break; case "Barber": text = "Clay Surgeon"; break; } EnemyData byEnemyName = Content.EnemyDataList.GetByEnemyName(text); if (byEnemyName == null) { Plugin.Logger.LogError((object)("Failed to find EnemyData from key name \"" + name + "\".")); return; } if (Plugin.InputActionsInstance.MonsterPrefixKey.IsPressed()) { EnemyHelper.SpawnEnemy(byEnemyName); } if (Plugin.InputActionsInstance.PlushiePrefixKey.IsPressed()) { PlushieManager.Instance.SpawnPlushies(byEnemyName.EnemyName); } } private static void OnSpawnRandomKeyPressed(CallbackContext context) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) if (!((CallbackContext)(ref context)).performed) { return; } Plugin.Instance.LogInfoExtended(((CallbackContext)(ref context)).action.name + " key pressed."); if (!Utils.CanPerformHotkeys()) { return; } if (DisableHotkeys && (IsMonsterPrefixKeyPressed || IsPlushiePrefixKeyPressed)) { MessageCanvas.Instance.ShowMessage_LocalClient("Hotkeys have been disabled by another mod", Color.red); Plugin.Logger.LogInfo((object)"Hotkeys have been disabled by another mod."); return; } if (IsMonsterPrefixKeyPressed) { EnemyHelper.SpawnRandomEnemy(); } if (IsPlushiePrefixKeyPressed) { PlushieManager.Instance.SpawnRandomPlushies(); } } } internal static class NetworkUtils { public static bool IsConnected { get { if ((Object)(object)NetworkManager.Singleton == (Object)null) { return false; } return NetworkManager.Singleton.IsConnectedClient; } } public static bool IsServer { get { if ((Object)(object)NetworkManager.Singleton == (Object)null) { return false; } return NetworkManager.Singleton.IsServer; } } public static bool IsHost { get { if ((Object)(object)NetworkManager.Singleton == (Object)null) { return false; } return NetworkManager.Singleton.IsHost; } } public static ulong GetLocalClientId() { return NetworkManager.Singleton.LocalClientId; } public static bool IsLocalClientId(ulong clientId) { return clientId == GetLocalClientId(); } public static bool IsNetworkPrefab(GameObject prefab) { foreach (NetworkPrefab prefab2 in NetworkManager.Singleton.NetworkConfig.Prefabs.Prefabs) { if ((Object)(object)prefab2.Prefab == (Object)(object)prefab) { return true; } } return false; } } internal static class PlayerUtils { public static PlayerControllerB GetLocalPlayerScript() { if ((Object)(object)GameNetworkManager.Instance == (Object)null) { return null; } return GameNetworkManager.Instance.localPlayerController; } public static bool IsLocalPlayer(PlayerControllerB playerScript) { return (Object)(object)playerScript == (Object)(object)GetLocalPlayerScript(); } public static PlayerControllerB GetPlayerScriptByClientId(ulong clientId) { PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null) && val.actualClientId == clientId) { return val; } } return null; } public static List<PlayerControllerB> GetPlayerScripts() { List<PlayerControllerB> list = new List<PlayerControllerB>(); PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null) && (val.isInHangarShipRoom || val.isInsideFactory || val.isInElevator)) { list.Add(val); } } return list; } public static PlayerControllerB GetRandomPlayerScript(bool onlyAlivePlayers = true, bool excludeLocalPlayer = false) { List<PlayerControllerB> list = new List<PlayerControllerB>(); foreach (PlayerControllerB playerScript in GetPlayerScripts()) { if ((!onlyAlivePlayers || !playerScript.isPlayerDead) && (!excludeLocalPlayer || !IsLocalPlayer(playerScript))) { list.Add(playerScript); } } if (list.Count == 0) { return null; } return list[Random.Range(0, list.Count)]; } } [BepInPlugin("com.github.zehsteam.MonsterHotkeys", "MonsterHotkeys", "2.3.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [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.MonsterHotkeys"); internal static Plugin Instance { get; private set; } internal static ManualLogSource Logger { get; private set; } internal static ConfigManager ConfigManager { get; private set; } internal static HotkeyInputClass InputActionsInstance { get; private set; } private void Awake() { if ((Object)(object)Instance == (Object)null) { Instance = this; } Logger = Logger.CreateLogSource("com.github.zehsteam.MonsterHotkeys"); Logger.LogInfo((object)"MonsterHotkeys has awoken!"); _harmony.PatchAll(typeof(GameNetworkManagerPatch)); _harmony.PatchAll(typeof(StartOfRoundPatch)); _harmony.PatchAll(typeof(RoundManagerPatch)); _harmony.PatchAll(typeof(HUDManagerPatch)); _harmony.PatchAll(typeof(EnemyAIPatch)); _harmony.PatchAll(typeof(RadMechAIPatch)); _harmony.PatchAll(typeof(ButlerEnemyAIPatch)); _harmony.PatchAll(typeof(LandminePatch)); if (CrowdControlProxy.Enabled) { CrowdControlProxy.PatchAll(_harmony); } Content.Load(); ConfigManager = new ConfigManager(); InputActionsInstance = new HotkeyInputClass(); HotkeyListener.SetupKeybindCallbacks(); Content.EnemyDataList.Initialize(); TwitchIntegrationManager.Initialize(); NetcodePatcherAwake(); } private void Start() { PlayerDamagePatcher.PatchAll(_harmony); } private void NetcodePatcherAwake() { try { Assembly executingAssembly = Assembly.GetExecutingAssembly(); Type[] types = executingAssembly.GetTypes(); Type[] array = types; foreach (Type type in array) { MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic); MethodInfo[] array2 = methods; foreach (MethodInfo methodInfo in array2) { try { object[] customAttributes = methodInfo.GetCustomAttributes(typeof(RuntimeInitializeOnLoadMethodAttribute), inherit: false); if (customAttributes.Length != 0) { try { methodInfo.Invoke(null, null); } catch (TargetInvocationException ex) { Logger.LogWarning((object)("Failed to invoke method " + methodInfo.Name + ": " + ex.Message)); } } } catch (Exception ex2) { Logger.LogWarning((object)("Error processing method " + methodInfo.Name + " in type " + type.Name + ": " + ex2.Message)); } } } } catch (Exception ex3) { Logger.LogError((object)("An error occurred in NetcodePatcherAwake: " + ex3.Message)); } } public void OnNewLevelLoaded() { InteriorHelper.SetInteriorType(); EnemyNametagManager.Instance?.Reset(); } public void SpawnMessageCanvas() { if (!((Object)(object)MessageCanvas.Instance != (Object)null)) { Object.Instantiate<GameObject>(Content.MessageCanvasPrefab); } } public void LogInfoExtended(object data) { LogExtended((LogLevel)16, data); } public void LogWarningExtended(object data) { LogExtended((LogLevel)4, data); } public void LogExtended(LogLevel level, object data) { //IL_0022: 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) if (ConfigManager == null || ConfigManager.ExtendedLogging == null) { Logger.Log(level, data); } else if (ConfigManager.ExtendedLogging.Value) { Logger.Log(level, data); } } } internal static class Utils { public static string GetEnumName<T>(T e) where T : Enum { return Enum.GetName(typeof(T), e) ?? string.Empty; } public static string GetPluginDirectoryPath() { return Path.GetDirectoryName(((BaseUnityPlugin)Plugin.Instance).Info.Location); } public static string GetConfigDirectoryPath() { return Paths.ConfigPath; } public static string GetGlobalConfigDirectoryPath() { return Path.Combine(Application.persistentDataPath, "MonsterHotkeys"); } public static ConfigFile CreateConfigFile(string directoryPath, string name = null, bool saveOnInit = false) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown BepInPlugin metadata = MetadataHelper.GetMetadata((object)Plugin.Instance); if (name == null) { name = metadata.GUID; } name += ".cfg"; return new ConfigFile(Path.Combine(directoryPath, name), saveOnInit, metadata); } public static ConfigFile CreateLocalConfigFile(string name = null, bool saveOnInit = false) { if (name == null) { name = "com.github.zehsteam.MonsterHotkeys-" + name; } return CreateConfigFile(Paths.ConfigPath, name, saveOnInit); } public static ConfigFile CreateGlobalConfigFile(string name = null, bool saveOnInit = false) { if (name == null) { name = "global"; } return CreateConfigFile(GetGlobalConfigDirectoryPath(), name, saveOnInit); } public static void LogStackTrace() { StackTrace stackTrace = new StackTrace(); for (int i = 1; i < stackTrace.FrameCount; i++) { StackFrame frame = stackTrace.GetFrame(i); MethodBase method = frame.GetMethod(); Type declaringType = method.DeclaringType; string name = method.Name; Plugin.Instance.LogInfoExtended($"Call stack depth {i}: {declaringType}.{name}"); } } public static bool RandomPercent(float percent) { if (percent <= 0f) { return false; } if (percent >= 100f) { return true; } return Random.value * 100f <= percent; } public static string GetFormattedString(string text) { if (string.IsNullOrWhiteSpace(text)) { return string.Empty; } return string.Join(" ", from word in text.Split(' ', StringSplitOptions.RemoveEmptyEntries) select $"{char.ToUpper(word[0])}{word.Substring(1).ToLower()}"); } public static bool CanPerformHotkeys() { PlayerControllerB localPlayerScript = PlayerUtils.GetLocalPlayerScript(); if ((Object)(object)localPlayerScript == (Object)null) { return false; } if (localPlayerScript.isPlayerDead) { return false; } if (localPlayerScript.isTypingChat) { return false; } if (localPlayerScript.quickMenuManager.isMenuOpen) { return false; } return true; } public static Coroutine StartCoroutine(IEnumerator routine) { if ((Object)(object)Plugin.Instance != (Object)null) { return ((MonoBehaviour)Plugin.Instance).StartCoroutine(routine); } if ((Object)(object)GameNetworkManager.Instance != (Object)null) { return ((MonoBehaviour)GameNetworkManager.Instance).StartCoroutine(routine); } Plugin.Logger.LogError((object)("Failed to start coroutine. " + routine)); return null; } public static bool TryParseValue<T>(string value, out T result) { try { if (typeof(T) == typeof(int) && int.TryParse(value, out var result2)) { result = (T)(object)result2; return true; } if (typeof(T) == typeof(float) && float.TryParse(value, out var result3)) { result = (T)(object)result3; return true; } if (typeof(T) == typeof(double) && double.TryParse(value, out var result4)) { result = (T)(object)result4; return true; } if (typeof(T) == typeof(bool) && bool.TryParse(value, out var result5)) { result = (T)(object)result5; return true; } if (typeof(T) == typeof(string)) { result = (T)(object)value; return true; } } catch { } result = default(T); return false; } public static string GetFormattedKeyValuePairMessage<T>(Dictionary<string, T> keyValuePair, int columns, string format, string separator = " ") { if (keyValuePair == null || keyValuePair.Count == 0) { return string.Empty; } int num = Mathf.CeilToInt((float)keyValuePair.Count / (float)columns); List<List<KeyValuePair<string, T>>> list = new List<List<KeyValuePair<string, T>>>(); for (int i = 0; i < columns; i++) { list.Add(keyValuePair.Skip(i * num).Take(num).ToList()); } List<int> list2 = list.Select((List<KeyValuePair<string, T>> column) => column.Any() ? column.Max((KeyValuePair<string, T> item) => item.Key.Length) : 0).ToList(); List<int> list3 = list.Select((List<KeyValuePair<string, T>> column) => column.Any() ? column.Max((KeyValuePair<string, T> item) => item.Value.ToString().Length) : 0).ToList(); StringBuilder stringBuilder = new StringBuilder(); for (int j = 0; j < num; j++) { for (int k = 0; k < columns; k++) { if (k < list.Count && j < list[k].Count) { KeyValuePair<string, T> keyValuePair2 = list[k][j]; string newValue = keyValuePair2.Key.PadRight(list2[k]); string newValue2 = keyValuePair2.Value.ToString().PadRight(list3[k]); string value = format.Replace("{key}", newValue).Replace("{value}", newValue2); stringBuilder.Append(value); if (k < columns - 1) { stringBuilder.Append(" | "); } } } stringBuilder.AppendLine(); } return stringBuilder.ToString().TrimEnd(); } public static bool IsChildOfComponent<T>(Transform transform, out T component) where T : Component { component = default(T); if ((Object)(object)transform == (Object)null || (Object)(object)transform.parent == (Object)null) { return false; } if (((Component)transform.parent).TryGetComponent<T>(ref component)) { return true; } return IsChildOfComponent<T>(transform.parent, out component); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "com.github.zehsteam.MonsterHotkeys"; public const string PLUGIN_NAME = "MonsterHotkeys"; public const string PLUGIN_VERSION = "2.3.1"; } } namespace com.github.zehsteam.MonsterHotkeys.Twitch { internal static class TwitchIntegrationManager { public static int SpawnPointsUsedThisDay; public const string UseSpawnPointText = "<color=#00FF00>Use !spawn to spawn your free enemy</color>"; private static Coroutine _playQueueCoroutine; private static bool _isPlayingQueue; public static List<TwitchCheerEvent> CheerQueue { get; private set; } = new List<TwitchCheerEvent>(); public static List<TwitchSubEvent> SubQueue { get; private set; } = new List<TwitchSubEvent>(); public static List<TwitchRaidEvent> RaidQueue { get; private set; } = new List<TwitchRaidEvent>(); public static Dictionary<string, int> AccumulatedBits { get; private set; } = new Dictionary<string, int>(); public static Dictionary<string, int> SpawnPoints { get; private set; } = new Dictionary<string, int>(); public static void Initialize() { try { API.OnMessage += HandleMessage; API.OnCheer += HandleCheer; API.OnSub += HandleSub; API.OnRaid += HandleRaid; LoadData(); Application.quitting += delegate { API.OnMessage -= HandleMessage; API.OnCheer -= HandleCheer; API.OnSub -= HandleSub; API.OnRaid -= HandleRaid; SaveData(); }; } catch (Exception arg) { Plugin.Logger.LogError((object)$"Failed to initialize TwitchIntegrationManager. {arg}"); } } private static void LoadData() { Plugin.Logger.LogInfo((object)"Loading saved TwitchIntegrationManager data..."); try { if (SaveHelper.TryLoadValue<string>("CheerQueue", SaveLocation.Global, out var value)) { Plugin.Instance.LogInfoExtended("Loaded CheerQueue JSON data:\n\n" + value); CheerQueue = JsonConvert.DeserializeObject<List<TwitchCheerEvent>>(value); } if (SaveHelper.TryLoadValue<string>("SubQueue", SaveLocation.Global, out value)) { Plugin.Instance.LogInfoExtended("Loaded SubQueue JSON data:\n\n" + value); SubQueue = JsonConvert.DeserializeObject<List<TwitchSubEvent>>(value); } if (SaveHelper.TryLoadValue<string>("RaidQueue", SaveLocation.Global, out value)) { Plugin.Instance.LogInfoExtended("Loaded RaidQueue JSON data:\n\n" + value); RaidQueue = JsonConvert.DeserializeObject<List<TwitchRaidEvent>>(value); } if (SaveHelper.TryLoadValue<string>("AccumulatedBits", SaveLocation.Global, out value)) { Plugin.Instance.LogInfoExtended("Loaded AccumulatedBits JSON data:\n\n" + value); AccumulatedBits = JsonConvert.DeserializeObject<Dictionary<string, int>>(value); } if (SaveHelper.TryLoadValue<string>("SpawnPoints", SaveLocation.Global, out value)) { Plugin.Instance.LogInfoExtended("Loaded SpawnPoints JSON data:\n\n" + value); SpawnPoints = JsonConvert.DeserializeObject<Dictionary<string, int>>(value); } Plugin.Logger.LogInfo((object)"Finished loading saved TwitchIntegrationManager data."); } catch (Exception arg) { Plugin.Logger.LogError((object)$"Failed to load TwitchIntegrationManager save data. {arg}"); } } public static void SaveData() { try { SaveHelper.SaveValue("CheerQueue", JsonConvert.SerializeObject((object)CheerQueue), SaveLocation.Global); SaveHelper.SaveValue("SubQueue", JsonConvert.SerializeObject((object)SubQueue), SaveLocation.Global); SaveHelper.SaveValue("RaidQueue", JsonConvert.SerializeObject((object)RaidQueue), SaveLocation.Global); SaveHelper.SaveValue("AccumulatedBits", JsonConvert.SerializeObject((object)AccumulatedBits), SaveLocation.Global); SaveHelper.SaveValue("SpawnPoints", JsonConvert.SerializeObject((object)SpawnPoints), SaveLocation.Global); Plugin.Logger.LogInfo((object)"Saved TwitchIntegrationManager data."); } catch (Exception arg) { Plugin.Logger.LogError((object)$"Failed to save TwitchIntegrationManager data. {arg}"); } } public static void PlayQueue(float initialDelay = 0f) { if (!Plugin.ConfigManager.TwitchIntegration_Enabled.Value) { return; } if ((Object)(object)ViewerSpawnEventHandler.Instance == (Object)null) { Plugin.Logger.LogWarning((object)"Failed to play Twitch event queue. ViewerSpawnEventHandler instance is null."); return; } if (_isPlayingQueue) { Plugin.Logger.LogWarning((object)"Failed to play Twitch event queue. Twitch event queue is already playing."); return; } if (_playQueueCoroutine != null) { ((MonoBehaviour)ViewerSpawnEventHandler.Instance).StopCoroutine(_playQueueCoroutine); } _playQueueCoroutine = ((MonoBehaviour)ViewerSpawnEventHandler.Instance).StartCoroutine(PlayQueueCoroutine(initialDelay)); } private static IEnumerator PlayQueueCoroutine(float initialDelay = 0f) { _isPlayingQueue = true; Plugin.Instance.LogInfoExtended("Started Twitch event queue."); yield return (object)new WaitForSeconds(initialDelay); if (!CanExecuteEvents()) { Plugin.Instance.LogInfoExtended("Finished Twitch event queue. No events could be executed."); _isPlayingQueue = false; yield break; } if (SubQueue.Count > 0 || CheerQueue.Count > 0 || RaidQueue.Count > 0) { MessageCanvas.Instance?.ShowMessage_LocalClient("Playing Twitch events from the queue"); } float delay = 0.5f; while (SubQueue.Count > 0 && CanExecuteEvents()) { TwitchSubEvent twitchSubEvent = SubQueue[0]; SubQueue.RemoveAt(0); HandleSub(twitchSubEvent); yield return (object)new WaitForSeconds(delay); } while (CheerQueue.Count > 0 && CanExecuteEvents()) { TwitchCheerEvent twitchCheerEvent = CheerQueue[0]; CheerQueue.RemoveAt(0); HandleCheer(twitchCheerEvent); yield return (object)new WaitForSeconds(delay); } while (RaidQueue.Count > 0 && CanExecuteEvents()) { TwitchRaidEvent twitchRaidEvent = RaidQueue[0]; RaidQueue.RemoveAt(0); HandleRaid(twitchRaidEvent); yield return (object)new WaitForSeconds(delay); } Plugin.Instance.LogInfoExtended("Finished Twitch event queue."); SaveData(); _isPlayingQueue = false; } private static void HandleMessage(TwitchMessage twitchMessage) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (Plugin.ConfigManager.TwitchIntegration_Enabled.Value) { TwitchCommandManager.ExecuteCommand(twitchMessage); } } private static void HandleCheer(TwitchCheerEvent cheerEvent) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.ConfigManager.TwitchIntegration_Enabled.Value || !Plugin.ConfigManager.TwitchCheerEvent_Enabled.Value) { return; } if (cheerEvent == null) { Plugin.Logger.LogError((object)"Failed to handle cheer. TwitchCheerEvent is null."); return; } if (!CanExecuteEvents()) { CheerQueue.Add(cheerEvent); SaveData(); MessageCanvas.Instance?.ShowMessage_LocalClient("Added Twitch cheer event from " + GetDisplayNameWithColor(((TwitchEvent)cheerEvent).User) + " to queue"); return; } TwitchUser user = ((TwitchEvent)cheerEvent).User; string userId = ((TwitchUser)(ref user)).UserId; user = ((TwitchEvent)cheerEvent).User; string displayName = ((TwitchUser)(ref user)).DisplayName; user = ((TwitchEvent)cheerEvent).User; ViewerData viewerData = new ViewerData(userId, displayName, ((TwitchUser)(ref user)).Color); if (cheerEvent.CheerAmount == Plugin.ConfigManager.TwitchCheerEvent_AmountToSpawnPlushies.Value) { Plugin.Instance.LogInfoExtended("HandleCheer:\n" + JsonConvert.SerializeObject((object)viewerData)); PlushieManager.Instance?.SpawnPlushiesFromViewer(viewerData, $"by cheering {cheerEvent.CheerAmount} bits"); return; } int accumulatedBits = GetAccumulatedBits(viewerData); int num = cheerEvent.CheerAmount + accumulatedBits; int value = Plugin.ConfigManager.TwitchCheerEvent_AmountToSpawnEnemy.Value; if (num < value) { SetAccumulatedBits(viewerData, num); return; } int spawnCount = Mathf.FloorToInt((float)(num / value)); int num2 = num % value; int num3 = num - num2; SetAccumulatedBits(viewerData, num2); string spawnReason = $"by cheering {num3} bits"; ViewerSpawnEvent viewerSpawnEvent = new ViewerSpawnEvent(viewerData, spawnCount, spawnReason); Plugin.Instance.LogInfoExtended("HandleCheer:\n" + JsonConvert.SerializeObject((object)viewerSpawnEvent)); ViewerSpawnEventHandler.Instance?.ExecuteViewerSpawnEventServerRpc(viewerSpawnEvent); } private static void HandleSub(TwitchSubEvent subEvent) { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Invalid comparison between Unknown and I4 //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Invalid comparison between Unknown and I4 //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Invalid comparison between Unknown and I4 //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Invalid comparison between Unknown and I4 //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Invalid comparison between Unknown and I4 //IL_0218: 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_0227: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.ConfigManager.TwitchIntegration_Enabled.Value || !Plugin.ConfigManager.TwitchSubEvent_Enabled.Value) { return; } if (subEvent == null) { Plugin.Logger.LogError((object)"Failed to handle sub. TwitchSubEvent is null."); return; } if (!CanExecuteEvents()) { SubQueue.Add(subEvent); SaveData(); MessageCanvas.Instance?.ShowMessage_LocalClient("Added Twitch sub event from " + GetDisplayNameWithColor(((TwitchEvent)subEvent).User) + " to queue"); return; } int num = Plugin.ConfigManager.TwitchSubEvent_EnemiesPerSub.Value; if ((int)subEvent.SubType == 3) { num *= subEvent.GiftCount; } if (subEvent.Tier == 2) { num *= Plugin.ConfigManager.TwitchSubEvent_Tier2EnemyMultiplier.Value; } else if (subEvent.Tier == 3) { num *= Plugin.ConfigManager.TwitchSubEvent_Tier3EnemyMultiplier.Value; } string spawnReason = string.Empty; if ((int)subEvent.SubType == 0) { spawnReason = ((!subEvent.IsPrime) ? $"by subbing at tier {subEvent.Tier}" : "by subbing with prime"); } else if ((int)subEvent.SubType == 1) { spawnReason = ((!subEvent.IsPrime) ? $"by resubbing at tier {subEvent.Tier} for {subEvent.Months} months" : "by resubbing with prime"); } else if ((int)subEvent.SubType == 2) { spawnReason = $"by gifting a tier {subEvent.Tier} sub to {subEvent.RecipientUser}"; } else if ((int)subEvent.SubType == 3) { spawnReason = $"by gifting {subEvent.GiftCount} tier {subEvent.Tier} subs"; } TwitchUser user = ((TwitchEvent)subEvent).User; string userId = ((TwitchUser)(ref user)).UserId; user = ((TwitchEvent)subEvent).User; string displayName = ((TwitchUser)(ref user)).DisplayName; user = ((TwitchEvent)subEvent).User; ViewerData viewer = new ViewerData(userId, displayName, ((TwitchUser)(ref user)).Color); ViewerSpawnEvent viewerSpawnEvent = new ViewerSpawnEvent(viewer, num, spawnReason); Plugin.Instance.LogInfoExtended("HandleSub:\n" + JsonConvert.SerializeObject((object)viewerSpawnEvent)); ViewerSpawnEventHandler.Instance?.ExecuteViewerSpawnEventServerRpc(viewerSpawnEvent); } private static void HandleRaid(TwitchRaidEvent raidEvent) { //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) if (Plugin.ConfigManager.TwitchIntegration_Enabled.Value && Plugin.ConfigManager.TwitchRaidEvent_Enabled.Value) { if (raidEvent == null) { Plugin.Logger.LogError((object)"Failed to handle raid. TwitchRaidEvent is null."); return; } if (!CanExecuteEvents()) { RaidQueue.Add(raidEvent); SaveData(); MessageCanvas.Instance?.ShowMessage_LocalClient("Added Twitch raid event from " + GetDisplayNameWithColor(((TwitchEvent)raidEvent).User) + " to queue"); return; } int value = Plugin.ConfigManager.TwitchRaidEvent_ViewersPerEnemy.Value; int value2 = Plugin.ConfigManager.TwitchRaidEvent_MaxSpawnCount.Value; int spawnCount = Mathf.Clamp(raidEvent.ViewerCount / value, 1, value2); string spawnReason = $"by raiding with {raidEvent.ViewerCount} viewers"; TwitchUser user = ((TwitchEvent)raidEvent).User; string userId = ((TwitchUser)(ref user)).UserId; user = ((TwitchEvent)raidEvent).User; string displayName = ((TwitchUser)(ref user)).DisplayName; user = ((TwitchEvent)raidEvent).User; ViewerData viewer = new ViewerData(userId, displayName, ((TwitchUser)(ref user)).Color); ViewerSpawnEvent viewerSpawnEvent = new ViewerSpawnEvent(viewer, spawnCount, spawnReason); Plugin.Instance.LogInfoExtended("HandleRaid:\n" + JsonConvert.SerializeObject((object)viewerSpawnEvent)); ViewerSpawnEventHandler.Instance?.ExecuteViewerSpawnEventServerRpc(viewerSpawnEvent); } } public static int GetAccumulatedBits(ViewerData viewer) { if (viewer == null) { return 0; } return GetAccumulatedBits(viewer.DisplayName); } public static int GetAccumulatedBits(string displayName) { return AccumulatedBits.GetValueOrDefault(displayName.ToLower(), 0); } public static void SetAccumulatedBits(ViewerData viewer, int value) { if (viewer != null) { if (value <= 0) { AccumulatedBits.Remove(viewer.DisplayName.ToLower()); } else { AccumulatedBits[viewer.DisplayName.ToLower()] = value; } SaveData(); } } public static void RewardSpawnPointsFromDeath(EnemyNametagType nametagType, string displayName, string color) { if (!Plugin.ConfigManager.TwitchIntegration_Enabled.Value || !Plugin.ConfigManager.SpawnPoints_Enabled.Value) { return; } switch (nametagType) { case EnemyNametagType.Default: return; case EnemyNametagType.CrowdControl: if (!Plugin.ConfigManager.CrowdControl_RewardSpawnPoints.Value) { return; } break; } int value = Plugin.ConfigManager.SpawnPoints_RewardPointsPerDeath.Value; if (value > 0) { AddSpawnPoints(displayName, value); int spawnPoints = GetSpawnPoints(displayName); string text = ((value == 1) ? "" : "s"); MessageCanvas.Instance?.ShowMessage_LocalClient(string.Format("<color={0}>{1}</color> just earned {2} spawn point{3} ({4} total) {5}", color, displayName, value, text, spawnPoints, "<color=#00FF00>Use !spawn to spawn your free enemy</color>")); } } public static void RewardSpawnPointsFromCrewmateDeath(ulong senderClientId, EnemyNametagType nametagType, string displayName, string color) { if (!Plugin.ConfigManager.TwitchIntegration_Enabled.Value || !Plugin.ConfigManager.SpawnPoints_Enabled.Value || NetworkUtils.IsLocalClientId(senderClientId)) { return; } switch (nametagType) { case EnemyNametagType.Default: return; case EnemyNametagType.CrowdControl: if (!Plugin.ConfigManager.CrowdControl_RewardSpawnPoints.Value) { return; } break; } int value = Plugin.ConfigManager.SpawnPoints_RewardPointsPerCrewmateDeath.Value; if (value > 0) { PlayerControllerB playerScriptByClientId = PlayerUtils.GetPlayerScriptByClientId(senderClientId); if (!((Object)(object)playerScriptByClientId == (Object)null)) { AddSpawnPoints(displayName, value); int spawnPoints = GetSpawnPoints(displayName); string text = ((value == 1) ? "" : "s"); MessageCanvas.Instance?.ShowMessage_LocalClient(string.Format("<color={0}>{1}</color> just earned {2} spawn point{3} by killing {4} ({5} total) {6}", color, displayName, value, text, playerScriptByClientId.playerUsername, spawnPoints, "<color=#00FF00>Use !spawn to spawn your free enemy</color>")); } } } public static int GetSpawnPoints(ViewerData viewer) { if (viewer == null) { return 0; } return GetSpawnPoints(viewer.DisplayName); } public static int GetSpawnPoints(string displayName) { return SpawnPoints.GetValueOrDefault(displayName.ToLower(), 0); } public static void SetSpawnPoints(ViewerData viewer, int value) { if (viewer != null) { SetSpawnPoints(viewer.DisplayName, value); } } public static void SetSpawnPoints(string displayName, int value) { if (value <= 0) { SpawnPoints.Remove(displayName.ToLower()); } else { SpawnPoints[displayName.ToLower()] = value; } SaveData(); } public static void AddSpawnPoints(ViewerData viewer, int amount = 1) { if (viewer != null) { AddSpawnPoints(viewer.DisplayName.ToLower()); } } public static void AddSpawnPoints(string displayName, int amount = 1) { int valueOrDefault = SpawnPoints.GetValueOrDefault(displayName.ToLower(), 0); int num = valueOrDefault + amount; if (num <= 0) { SpawnPoints.Remove(displayName.ToLower()); } else { SpawnPoints[displayName.ToLower()] = num; } SaveData(); } public static void OnLocalDisconnect() { SpawnPointsUsedThisDay = 0; } public static void OnDayEnded() { SpawnPointsUsedThisDay = 0; } public static bool CanExecuteEvents() { return EnemyHelper.CanSpawnEnemy(); } public static string GetDisplayNameWithColor(TwitchUser user) { string text = ((TwitchUser)(ref user)).Color; if (string.IsNullOrEmpty(text)) { text = "#FFFFFF"; } return "<color=" + text + ">" + ((TwitchUser)(ref user)).DisplayName + "</color>"; } public static string GetDisplayNameWithColor(ViewerData viewer) { if (viewer == null) { return string.Empty; } string text = viewer.Color; if (string.IsNullOrEmpty(text)) { text = "#FFFFFF"; } return "<color=" + text + ">" + viewer.DisplayName + "</color>"; } } } namespace com.github.zehsteam.MonsterHotkeys.Twitch.Commands { internal abstract class TwitchCommand { private readonly Dictionary<string, float> _timesSinceLastUserExecution = new Dictionary<string, float>(); private float _timeSinceLastExecution; public virtual bool IsCommand(TwitchMessage twitchMessage) { return false; } public void PreExecute(TwitchMessage twitchMessage) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) TwitchUser user = ((TwitchMessage)(ref twitchMessage)).User; if (!IsOnCooldown(((TwitchUser)(ref user)).DisplayName) && Execute(twitchMessage)) { user = ((TwitchMessage)(ref twitchMessage)).User; UpdateCooldowns(((TwitchUser)(ref user)).DisplayName); } } protected virtual bool Execute(TwitchMessage twitchMessage) { return true; } protected virtual float GetUserCooldown() { return 0f; } protected virtual float GetGlobalCooldown() { return 0f; } private void UpdateCooldowns(string displayName) { _timesSinceLastUserExecution[displayName.ToLower()] = Time.realtimeSinceStartup; _timeSinceLastExecution = Time.realtimeSinceStartup; } protected bool IsOnCooldown(string displayName) { if (IsOnUserCooldown(displayName)) { return true; } if (IsOnGlobalCooldown()) { return true; } return false; } protected bool IsOnUserCooldown(string displayName) { float userCooldown = GetUserCooldown(); if (userCooldown <= 0f) { return false; } float valueOrDefault = _timesSinceLastUserExecution.GetValueOrDefault(displayName.ToLower(), 0f); return Time.realtimeSinceStartup - valueOrDefault <= userCooldown; } protected bool IsOnGlobalCooldown() { float globalCooldown = GetGlobalCooldown(); if (globalCooldown <= 0f) { return false; } return Time.realtimeSinceStartup - _timeSinceLastExecution <= globalCooldown; } protected static bool IsModeratorOrHigher(TwitchUser twitchUser) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) return ((TwitchUser)(ref twitchUser)).IsModerator || ((TwitchUser)(ref twitchUser)).IsBroadcaster || IsDeveloper(twitchUser); } protected static bool IsDeveloper(TwitchUser twitchUser) { return ((TwitchUser)(ref twitchUser)).DisplayName.Equals("CritHaxXoG", StringComparison.OrdinalIgnoreCase); } } internal static class TwitchCommandManager { public static List<TwitchCommand> TwitchCommands { get; private set; } static TwitchCommandManager() { TwitchCommands = new List<TwitchCommand>(); TwitchCommands.Add(new TwitchSpawnCommand()); TwitchCommands.Add(new TwitchGiveSpawnCommand()); TwitchCommands.Add(new TwitchViewSpawnCommand()); TwitchCommands.Add(new TwitchViewAllSpawnCommand()); TwitchCommands.Add(new TwitchViewBitsCommand()); TwitchCommands.Add(new TwitchViewAllBitsCommand()); TwitchCommands.Add(new TwitchDevSpawnCommand()); TwitchCommands.Add(new TwitchDevPlushiesCommand()); } public static void ExecuteCommand(TwitchMessage twitchMessage) { //IL_001c: 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 (((TwitchMessage)(ref twitchMessage)).Message.StartsWith("!") && TryGetTwitchCommand(twitchMessage, out var twitchCommand)) { twitchCommand.PreExecute(twitchMessage); } } private static TwitchCommand GetTwitchCommand(TwitchMessage twitchMessage) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) foreach (TwitchCommand twitchCommand in TwitchCommands) { if (twitchCommand.IsCommand(twitchMessage)) { return twitchCommand; } } return null; } private static bool TryGetTwitchCommand(TwitchMessage twitchMessage, out TwitchCommand twitchCommand) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) twitchCommand = GetTwitchCommand(twitchMessage); return twitchCommand != null; } } internal class TwitchDevPlushiesCommand : TwitchCommand { public override bool IsCommand(TwitchMessage twitchMessage) { if (((TwitchMessage)(ref twitchMessage)).Message.Equals("!devplushies", StringComparison.OrdinalIgnoreCase)) { return true; } return false; } protected override float GetUserCooldown() { return base.GetUserCooldown(); } protected override float GetGlobalCooldown() { return base.GetGlobalCooldown(); } protected override bool Execute(TwitchMessage twitchMessage) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (!TwitchIntegrationManager.CanExecuteEvents()) { return false; } if (!TwitchCommand.IsDeveloper(((TwitchMessage)(ref twitchMessage)).User)) { return false; } TwitchUser user = ((TwitchMessage)(ref twitchMessage)).User; string userId = ((TwitchUser)(ref user)).UserId; user = ((TwitchMessage)(ref twitchMessage)).User; string displayName = ((TwitchUser)(ref user)).DisplayName; user = ((TwitchMessage)(ref twitchMessage)).User; ViewerData viewerData = new ViewerData(userId, displayName, ((TwitchUser)(ref user)).Color); Plugin.Instance.LogInfoExtended("TwitchDevPlushiesCommand:\n" + JsonConvert.SerializeObject((object)viewerData)); PlushieManager.Instance?.SpawnPlushiesFromViewer(viewerData, "by being the mod developer"); return true; } } internal class TwitchDevSpawnCommand : TwitchCommand { public override bool IsCommand(TwitchMessage twitchMessage) { if (((TwitchMessage)(ref twitchMessage)).Message.StartsWith("!devspawn", StringComparison.OrdinalIgnoreCase)) { return true; } return false; } protected override float GetUserCooldown() { return base.GetUserCooldown(); } protected override float GetGlobalCooldown() { return base.GetGlobalCooldown(); } protected override bool Execute(TwitchMessage twitchMessage) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) if (!TwitchIntegrationManager.CanExecuteEvents()) { return false; } if (!TwitchCommand.IsDeveloper(((TwitchMessage)(ref twitchMessage)).User)) { return false; } int result = 1; string[] array = ((TwitchMessage)(ref twitchMessage)).Message.Split(' '); if (array.Length >= 2 && !int.TryParse(array[1], out result)) { return false; } if (result <= 0) { return false; } result = Mathf.Min(result, 25); string spawnReason = "by being the mod developer"; TwitchUser user = ((TwitchMessage)(ref twitchMessage)).User; string userId = ((TwitchUser)(ref user)).UserId; user = ((TwitchMessage)(ref twitchMessage)).User; string displayName = ((TwitchUser)(ref user)).DisplayName; user = ((TwitchMessage)(ref twitchMessage)).User; ViewerData viewer = new ViewerData(userId, displayName, ((TwitchUser)(ref user)).Color); ViewerSpawnEvent viewerSpawnEvent = new ViewerSpawnEvent(viewer, result, spawnReason); Plugin.Instance.LogInfoExtended("TwitchDevSpawnCommand:\n" + JsonConvert.SerializeObject((object)viewerSpawnEvent)); ViewerSpawnEventHandler.Instance?.ExecuteViewerSpawnEventServerRpc(viewerSpawnEvent); return true; } } internal class TwitchGiveSpawnCommand : TwitchCommand { public override bool IsCommand(TwitchMessage twitchMessage) { if (((TwitchMessage)(ref twitchMessage)).Message.StartsWith("!givespawn", StringComparison.OrdinalIgnoreCase)) { return true; } return false; } protected override float GetUserCooldown() { return base.GetUserCooldown(); } protected override float GetGlobalCooldown() { return base.GetGlobalCooldown(); } protected override bool Execute(TwitchMessage twitchMessage) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.ConfigManager.SpawnPoints_Enabled.Value) { return false; } if (!TwitchCommand.IsModeratorOrHigher(((TwitchMessage)(ref twitchMessage)).User)) { return false; } string[] array = ((TwitchMessage)(ref twitchMessage)).Message.Split(' '); if (array.Length < 2) { return false; } string text = array[1].Replace("@", ""); if (text.Length > 25) { return false; } int result = 1; if (text.Length < 4) { TwitchUser user = ((TwitchMessage)(ref twitchMessage)).User; text = ((TwitchUser)(ref user)).DisplayName; if (!int.TryParse(array[1], out result)) { return false; } } else if (array.Length >= 3) { int.TryParse(array[2], out result); } TwitchIntegrationManager.AddSpawnPoints(text, result); int spawnPoints = TwitchIntegrationManager.GetSpawnPoints(text); string text2 = ((result == 1) ? "" : "s"); MessageCanvas.Instance?.ShowMessage_LocalClient(string.Format("{0} gave {1} {2} spawn point{3} ({4} total) {5}", TwitchIntegrationManager.GetDisplayNameWithColor(((TwitchMessage)(ref twitchMessage)).User), text, result, text2, spawnPoints, "<color=#00FF00>Use !spawn to spawn your free enemy</color>")); return true; } } internal class TwitchSpawnCommand : TwitchCommand { public override bool IsCommand(TwitchMessage twitchMessage) { if (((TwitchMessage)(ref twitchMessage)).Message.StartsWith("!spawn", StringComparison.OrdinalIgnoreCase)) { return true; } return false; } protected override float GetUserCooldown() { return base.GetUserCooldown(); } protected override float GetGlobalCooldown() { return base.GetGlobalCooldown(); } protected override bool Execute(TwitchMessage twitchMessage) { //IL_003b: 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_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.ConfigManager.SpawnPoints_Enabled.Value) { return false; } if (!TwitchIntegrationManager.CanExecuteEvents()) { return false; } TwitchUser user = ((TwitchMessage)(ref twitchMessage)).User; int spawnPoints = TwitchIntegrationManager.GetSpawnPoints(((TwitchUser)(ref user)).DisplayName); if (spawnPoints <= 0) { return false; } int value = Plugin.ConfigManager.SpawnPoints_MaxSpawnsPerDay.Value; int num = value - TwitchIntegrationManager.SpawnPointsUsedThisDay; if (num <= 0) { MessageCanvas.Instance?.ShowMessage_LocalClient("The max amount of spawn points have been redeemed for this day"); return false; } int result = 1; string[] array = ((TwitchMessage)(ref twitchMessage)).Message.Split(' '); if (array.Length >= 2) { int.TryParse(array[1], out result); } if (result <= 0) { return false; } if (result > spawnPoints) { result = spawnPoints; } if (result > num) { result = num; } int num2 = spawnPoints - result; user = ((TwitchMessage)(ref twitchMessage)).User; TwitchIntegrationManager.SetSpawnPoints(((TwitchUser)(ref user)).DisplayName, num2); TwitchIntegrationManager.SpawnPointsUsedThisDay += result; string arg = ((result == 1) ? "" : "s"); string spawnReason = $"by redeeming {result} spawn point{arg} ({num2} remaining)"; user = ((TwitchMessage)(ref twitchMessage)).User; string userId = ((TwitchUser)(ref user)).UserId; user = ((TwitchMessage)(ref twitchMessage)).User; string displayName = ((TwitchUser)(ref user)).DisplayName; user = ((TwitchMessage)(ref twitchMessage)).User; ViewerData viewer = new ViewerData(userId, displayName, ((TwitchUser)(ref user)).Color); ViewerSpawnEvent viewerSpawnEvent = new ViewerSpawnEvent(viewer, result, spawnReason); Plugin.Instance.LogInfoExtended("TwitchSpawnCommand:\n" + JsonConvert.SerializeObject((object)viewerSpawnEvent)); ViewerSpawnEventHandler.Instance?.ExecuteViewerSpawnEventServerRpc(viewerSpawnEvent); return true; } } internal class TwitchViewAllBitsCommand : TwitchCommand { public override bool IsCommand(TwitchMessage twitchMessage) { if (((TwitchMessage)(ref twitchMessage)).Message.Equals("!viewallbits", StringComparison.OrdinalIgnoreCase)) { return true; } return false; } protected override float GetUserCooldown() { return base.GetUserCooldown(); } protected override float GetGlobalCooldown() { return base.GetGlobalCooldown(); } protected override bool Execute(TwitchMessage twitchMessage) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) if (!TwitchCommand.IsModeratorOrHigher(((TwitchMessage)(ref twitchMessage)).User)) { return false; } if (TwitchIntegrationManager.AccumulatedBits.Count == 0) { MessageCanvas.Instance?.ShowMessage_LocalClient("Nobody has accumulated any bits"); return false; } int columns = 1; if (TwitchIntegrationManager.AccumulatedBits.Count >= 20) { columns = 3; } else if (TwitchIntegrationManager.AccumulatedBits.Count >= 10) { columns = 2; } Dictionary<string, int> keyValuePair = TwitchIntegrationManager.AccumulatedBits.OrderByDescending((KeyValuePair<string, int> x) => x.Value).ToDictionary((KeyValuePair<string, int> a) => a.Key, (KeyValuePair<string, int> b) => b.Value); MessageCanvas.Instance?.ShowMessage_LocalClient(Utils.GetFormattedKeyValuePairMessage(keyValuePair, columns, "{key} {value} bits")); return true; } } internal class TwitchViewAllSpawnCommand : TwitchCommand { public override bool IsCommand(TwitchMessage twitchMessage) { if (((TwitchMessage)(ref twitchMessage)).Message.Equals("!viewallspawn", StringComparison.OrdinalIgnoreCase)) { return true; } return false; } protected override float GetUserCooldown() { return base.GetUserCooldown(); } protected override float GetGlobalCooldown() { return base.GetGlobalCooldown(); } protected override bool Execute(TwitchMessage twitchMessage) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.ConfigManager.SpawnPoints_Enabled.Value) { return false; } if (!TwitchCommand.IsModeratorOrHigher(((TwitchMessage)(ref twitchMessage)).User)) { return false; } if (TwitchIntegrationManager.SpawnPoints.Count == 0) { MessageCanvas.Instance?.ShowMessage_LocalClient("Nobody has spawn points"); return false; } int columns = 1; if (TwitchIntegrationManager.SpawnPoints.Count >= 20) { columns = 3; } else if (TwitchIntegrationManager.SpawnPoints.Count >= 10) { columns = 2; } Dictionary<string, int> keyValuePair = TwitchIntegrationManager.SpawnPoints.OrderByDescending((KeyValuePair<string, int> x) => x.Value).ToDictionary((KeyValuePair<string, int> a) => a.Key, (KeyValuePair<string, int> b) => b.Value); MessageCanvas.Instance?.ShowMessage_LocalClient(Utils.GetFormattedKeyValuePairMessage(keyValuePair, columns, "{key} {value} sp")); return true; } } internal class TwitchViewBitsCommand : TwitchCommand { public override bool IsCommand(TwitchMessage twitchMessage) { if (((TwitchMessage)(ref twitchMessage)).Message.StartsWith("!viewbits", StringComparison.OrdinalIgnoreCase)) { return true; } return false; } protected override float GetUserCooldown() { return base.GetUserCooldown(); } protected override float GetGlobalCooldown() { return base.GetGlobalCooldown(); } protected override bool Execute(TwitchMessage twitchMessage) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_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) TwitchUser user = ((TwitchMessage)(ref twitchMessage)).User; string text = ((TwitchUser)(ref user)).DisplayName; user = ((TwitchMessage)(ref twitchMessage)).User; string arg = ((TwitchUser)(ref user)).Color; string[] array = ((TwitchMessage)(ref twitchMessage)).Message.Split(' '); if (array.Length > 1) { text = array[1].Replace("@", ""); arg = "#FFFFFF"; } int accumulatedBits = TwitchIntegrationManager.GetAccumulatedBits(text); MessageCanvas.Instance?.ShowMessage_LocalClient($"<color={arg}>{text}</color> has {accumulatedBits} accumulated bits"); return true; } } internal class TwitchViewSpawnCommand : TwitchCommand { public override bool IsCommand(TwitchMessage twitchMessage) { if (((TwitchMessage)(ref twitchMessage)).Message.StartsWith("!viewspawn", StringComparison.OrdinalIgnoreCase)) { return true; } return false; } protected override float GetUserCooldown() { return base.GetUserCooldown(); } protected override float GetGlobalCooldown() { return base.GetGlobalCooldown(); } protected override bool Execute(TwitchMessage twitchMessage) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.ConfigManager.SpawnPoints_Enabled.Value) { return false; } TwitchUser user = ((TwitchMessage)(ref twitchMessage)).User; string text = ((TwitchUser)(ref user)).DisplayName; user = ((TwitchMessage)(ref twitchMessage)).User; string text2 = ((TwitchUser)(ref user)).Color; string[] array = ((TwitchMessage)(ref twitchMessage)).Message.Split(' '); if (array.Length > 1) { text = array[1].Replace("@", ""); text2 = "#FFFFFF"; } int spawnPoints = TwitchIntegrationManager.GetSpawnPoints(text); string text3 = ((spawnPoints == 1) ? "" : "s"); MessageCanvas.Instance?.ShowMessage_LocalClient($"<color={text2}>{text}</color> has {spawnPoints} spawn point{text3}"); return true; } } } namespace com.github.zehsteam.MonsterHotkeys.Patches { [HarmonyPatch(typeof(ButlerEnemyAI))] internal static class ButlerEnemyAIPatch { [HarmonyTargetMethod] private static MethodBase TargetMethod() { Type type = typeof(ButlerEnemyAI).Assembly.GetTypes().FirstOrDefault((Type t) => t.Name.Contains("ButlerBlowUpAndPop") && t.Name.Contains("d__")); if (type == null) { Plugin.Logger.LogError((object)"[ButlerEnemyAIPatch] Failed to locate state machine class for ButlerBlowUpAndPop."); return null; } Plugin.Instance.LogInfoExtended("[ButlerEnemyAIPatch] Found state machine class: " + type.FullName); MethodInfo method = type.GetMethod("MoveNext", BindingFlags.Instance | BindingFlags.NonPublic); if (method == null) { Plugin.Logger.LogError((object)("[ButlerEnemyAIPatch] Failed to locate MoveNext method in " + type.FullName)); return null; } Plugin.Instance.LogInfoExtended("[ButlerEnemyAIPatch] Found MoveNext method: " + method.Name); return method; } [HarmonyTranspiler] private static IEnumerable<CodeInstruction> ButlerBlowUpAndPopTranspiler(IEnumerable<CodeInstruction> instructions) { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Expected O, but got Unknown MethodInfo method = typeof(RoundManager).GetMethod("SpawnEnemyGameObject"); MethodInfo method2 = typeof(ButlerHelper).GetMethod("SpawnButlerBees"); if (method == null || method2 == null) { Plugin.Logger.LogError((object)"[ButlerEnemyAIPatch] Failed to find required methods for transpiler."); return instructions; } List<CodeInstruction> list = new List<CodeInstruction>(); foreach (CodeInstruction instruction in instructions) { if ((instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) && instruction.operand is MethodInfo methodInfo && methodInfo == method) { list.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null)); list.Add(new CodeInstruction(OpCodes.Call, (object)method2)); Plugin.Instance.LogInfoExtended("[ButlerEnemyAIPatch] Replaced " + method.Name + " call with " + method2.Name + "."); } else { list.Add(instruction); } } return list.AsEnumerable(); } } [HarmonyPatch(typeof(EnemyAI))] internal static class EnemyAIPatch { [HarmonyPatch("KillEnemy")] [HarmonyPrefix] private static void KillEnemyPatchPrefix(ref EnemyAI __instance) { if (__instance.enemyType.canDie) { EnemyNametagManager.Instance?.DisableNametag_LocalClient(__instance); } } [HarmonyPatch("OnDestroy")] [HarmonyPostfix] private static void OnDestroyPatch(ref EnemyAI __instance) { EnemyNametagManager.Instance?.DespawnNametag_LocalClient(__instance); } } [HarmonyPatch(typeof(GameNetworkManager))] internal static class GameNetworkManagerPatch { [HarmonyPatch("Start")] [HarmonyPostfix] private static void StartPatch() { AddNetworkPrefabs(); } private static void AddNetworkPrefabs() { AddNetworkPrefab(Content.NetworkHandlerPrefab); } private static void AddNetworkPrefab(GameObject prefab) { if ((Object)(object)prefab == (Object)null) { Plugin.Logger.LogError((object)"Failed to register network prefab. GameObject is null."); return; } NetworkManager.Singleton.AddNetworkPrefab(prefab); Plugin.Logger.LogInfo((object)("Registered \"" + ((Object)prefab).name + "\" network prefab.")); } } [HarmonyPatch(typeof(HUDManager))] internal static class HUDManagerPatch { [HarmonyPatch("Start")] [HarmonyPostfix] private static void StartPatch() { Plugin.Instance.SpawnMessageCanvas(); } } [HarmonyPatch(typeof(Landmine))] internal static class LandminePatch { [HarmonyPatch("SpawnExplosion")] [HarmonyTranspiler] private static IEnumerable<CodeInstruction> SpawnExplosionTranspiler(IEnumerable<CodeInstruction> instructions) { //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Expected O, but got Unknown //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Expected O, but got Unknown //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Expected O, but got Unknown //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Expected O, but got Unknown MethodInfo method = typeof(PlayerControllerB).GetMethod("DamagePlayer"); MethodInfo method2 = typeof(PlayerControllerB).GetMethod("KillPlayer"); MethodInfo method3 = typeof(PlayerHelper).GetMethod("DamagePlayerWithCaller"); MethodInfo method4 = typeof(PlayerHelper).GetMethod("KillPlayerWithCaller"); MethodInfo methodInfo = typeof(LandmineHelper).GetProperty("SpawnExplosionCaller")?.GetGetMethod(); if (method == null || method2 == null || method3 == null || method4 == null || methodInfo == null) { Plugin.Logger.LogError((object)"[LandminePatch] Failed to find required methods for transpiler."); return instructions; } List<CodeInstruction> list = new List<CodeInstruction>(); foreach (CodeInstruction instruction in instructions) { if ((instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) && instruction.operand is MethodInfo methodInfo2) { if (methodInfo2 == method) { list.Add(new CodeInstruction(OpCodes.Call, (object)methodInfo)); list.Add(new CodeInstruction(OpCodes.Call, (object)method3)); Plugin.Instance.LogInfoExtended("[LandminePatch] Replaced DamagePlayer call with " + method3.Name + "."); continue; } if (methodInfo2 == method2) { list.Add(new CodeInstruction(OpCodes.Call, (object)methodInfo)); list.Add(new CodeInstruction(OpCodes.Call, (object)method4)); Plugin.Instance.LogInfoExtended("[LandminePatch] Replaced KillPlayer call with " + method4.Name + "."); continue; } } list.Add(instruction); } return list.AsEnumerable(); } } internal static class PlayerDamagePatcher { private static readonly MethodInfo _originalDamagePlayer = typeof(PlayerControllerB).GetMethod("DamagePlayer"); private static readonly MethodInfo _originalKillPlayer = typeof(PlayerControllerB).GetMethod("KillPlayer"); private static readonly MethodInfo _replacementDamagePlayer = typeof(PlayerHelper).GetMethod("DamagePlayerWithCaller"); private static readonly MethodInfo _replacementKillPlayer = typeof(PlayerHelper).GetMethod("KillPlayerWithCaller"); private static bool _patched; private static int _transpilersCreated; public static void PatchAll(Harmony harmony) { if (_patched) { return; } _patched = true; LogInfo("Running patcher..."); if (_originalDamagePlayer == null || _originalKillPlayer == null || _replacementDamagePlayer == null || _replacementKillPlayer == null) { LogError("Required methods not found for patcher."); return; } IEnumerable<Assembly> validAssemblies = GetValidAssemblies(); LogInfo($"Found {validAssemblies.Count()} valid assemblies."); foreach (Assembly item in validAssemblies) { LogInfoExtended("Found assembly: " + item.FullName); } _transpilersCreated = 0; Parallel.ForEach(validAssemblies, delegate(Assembly assembly) { PatchAssembly(assembly, harmony); }); LogInfo($"Created {_transpilersCreated} transpilers."); LogInfo("Patcher finished."); } private static void PatchAssembly(Assembly assembly, Harmony harmony) { LogInfoExtended("Patching assembly: " + assembly.FullName); IEnumerable<Type> validClasses = GetValidClasses(assembly); Parallel.ForEach(validClasses, delegate(Type type) { PatchClass(type, harmony); }); } private static void PatchClass(Type type, Harmony harmony) { LogInfoExtended("Patching class: " + type.FullName); IEnumerable<MethodInfo> validMethods = GetValidMethods(type); Parallel.ForEach(validMethods, delegate(MethodInfo method) { PatchMethod(method, harmony); }); } private static void PatchMethod(MethodInfo method, Harmony harmony) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown try { if (ReflectionHelper.IsCoroutineMethod(method)) { MethodInfo coroutineMoveNextMethod = ReflectionHelper.GetCoroutineMoveNextMethod(method); if (coroutineMoveNextMethod == null) { return; } harmony.Patch((MethodBase)coroutineMoveNextMethod, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(PlayerDamagePatcher), "Transpiler", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); LogInfoExtended($"Patched coroutine: {coroutineMoveNextMethod}"); } else { harmony.Patch((MethodBase)method, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(PlayerDamagePatcher), "Transpiler", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); } LogInfoExtended("Patched method: " + method.Name); Interlocked.Increment(ref _transpilersCreated); } catch (Exception arg) { LogError($"Failed to patch method: {method}\n\n{arg}"); } } public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Expected O, but got Unknown //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Expected O, but got Unknown //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Expected O, but got Unknown if (_originalDamagePlayer == null || _originalKillPlayer == null || _replacementDamagePlayer == null || _replacementKillPlayer == null) { LogError("Required methods not found for transpiler."); return instructions; } List<CodeInstruction> list = new List<CodeInstruction>(); foreach (CodeInstruction instruction in instructions) { if ((instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) && instruction.operand is MethodInfo methodInfo) { if (methodInfo == _originalDamagePlayer) { list.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null)); list.Add(new CodeInstruction(OpCodes.Call, (object)_replacementDamagePlayer)); LogInfoExtended("Replaced " + _originalDamagePlayer.Name + " call with " + _replacementDamagePlayer.Name + "."); continue; } if (methodInfo == _originalKillPlayer) { list.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null)); list.Add(new CodeInstruction(OpCodes.Call, (object)_replacementKillPlayer)); LogInfoExtended("Replaced " + _originalKillPlayer.Name + " call with " + _replacementKillPlayer.Name + "."); continue; } } list.Add(instruction); } return list.AsEnumerable(); } private static IEnumerable<Assembly> GetValidAssemblies() { return AppDomain.CurrentDomain.GetAssemblies().Where(IsValidAssembly); } private static bool IsValidAssembly(Assembly assembly) { if (assembly == null || assembly.IsDynamic) { return false; } if (assembly == Assembly.GetExecutingAssembly()) { return false; } if (assembly.FullName.StartsWithAny(new string[4] { "System.", "giosuel.Imperium", "MMHOOK_", "UnityExplorer." }, StringComparison.OrdinalIgnoreCase)) { return false; } if (assembly.Location.StartsWith(Paths.PluginPath, StringComparison.OrdinalIgnoreCase)) { return true; } if (assembly.FullName.StartsWith("Assembly-CSharp")) { return true; } return false; } private static IEnumerable<Type> GetValidClasses(Assembly assembly) { if (assembly == null) { return Array.Empty<Type>(); } try { return assembly.GetTypes().Where(IsValidClass); } catch (ReflectionTypeLoadException ex) { LogWarningExtended("Error loading types from assembly " + assembly.FullName + ": " + ex.Message); return Array.Empty<Type>(); } } private static bool IsValidClass(Type type) { if (type == null) { return false; } if (!type.IsClass || type.IsAbstract) { return false; } if (type == typeof(PlayerControllerB)) { return false; } if (type.FullName.StartsWithAny(new string[2] { "DunGen.", "DigitalRuby.ThunderAndLightning." }, StringComparison.OrdinalIgnoreCase)) { return false; } if (type.Name.Contains("TurretBehaviour")) { return true; } if (typeof(EnemyAI).IsAssignableFrom(type) || typeof(ShotgunItem).IsAssignableFrom(type)) { return true; } return false; } private static IEnumerable<MethodInfo> GetValidMethods(Type type) { return type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(IsValidMethod); } private static bool IsValidMethod(MethodInfo method) { if (method == null) { return false; } if (method.IsGenericMethod || method.DeclaringType.IsGenericType) { return false; } return ILHelper.MethodCallsMethods(method, new MethodInfo[2] { _originalDamagePlayer, _originalKillPlayer }); } private static void LogInfo(object data) { Log((LogLevel)16, data); } private static void LogError(object data) { Log((LogLevel)2, data); } private static void Log(LogLevel logLevel, object data) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) Plugin.Logger.Log(logLevel, (object)$"[PlayerDamagePatcher] {data}"); } private static void LogInfoExtended(object data) { LogExtended((LogLevel)16, data); } private static void LogWarningExtended(object data) { LogExtended((LogLevel)4, data); } private static void LogExtended(LogLevel logLevel, object data) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) Plugin.Instance.LogExtended(logLevel, $"[PlayerDamagePatcher] {data}"); } } [HarmonyPatch(typeof(RadMechAI))] internal static class RadMechAIPatch { [HarmonyPatch("SetExplosion")] [HarmonyTranspiler] private static IEnumerable<CodeInstruction> SetExplosionTranspiler(IEnumerable<CodeInstruction> instructions) { return ReplaceLandmineSpawnExplosionTranspiler(instructions); } [HarmonyPatch("DoFootstepCycle")] [HarmonyTranspiler] private static IEnumerable<CodeInstruction> DoFootstepCycleTranspiler(IEnumerable<CodeInstruction> instructions) { return ReplaceLandmineSpawnExplosionTranspiler(instructions); } private static IEnumerable<CodeInstruction> ReplaceLandmineSpawnExplosionTranspiler(IEnumerable<CodeInstruction> instructions) { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Expected O, but got Unknown MethodInfo method = typeof(Landmine).GetMethod("SpawnExplosion"); MethodInfo method2 = typeof(LandmineHelper).GetMethod("SpawnExplosionWithCaller"); if (method == null || method2 == null) { Plugin.Logger.LogError((object)"[RadMechAIPatch] Failed to find required methods for transpiler."); return instructions; } List<CodeInstruction> list = new List<CodeInstruction>(); foreach (CodeInstruction instruction in instructions) { if ((instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) && instruction.operand is MethodInfo methodInfo && methodInfo == method) { list.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null)); list.Add(new CodeInstruction(OpCodes.Call, (object)method2)); Plugin.Instance.LogInfoExtended("[RadMechAIPatch] Replaced " + method.Name + " call with " + method2.Name + "."); } else { list.Add(instruction); } } return list.AsEnumerable(); } } [HarmonyPatch(typeof(RoundManager))] internal static class RoundManagerPatch { private static bool _shipArrived; private static float _timeSinceShipArrived; [HarmonyPatch("LoadNewLevel")] [HarmonyPostfix] private static void LoadNewLevelPatch() { Plugin.Instance.OnNewLevelLoaded(); } [HarmonyPatch("GenerateNewLevelClientRpc")] [HarmonyPrefix] private static void GenerateNewLevelClientRpcPatch() { if (!NetworkUtils.IsServer) { Plugin.Instance.OnNewLevelLoaded(); } } [HarmonyPatch("F