Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Unified Chaos Pack v0.0.15
BepInEx/plugins/TeamChaos.UnifiedChaosPack.dll
Decompiled 2 years 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.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using CSync.Lib; using CSync.Util; using FacilityMeltdown; using GameNetcodeStuff; using HarmonyLib; using JetBrains.Annotations; using LCSoundTool; using LethalConfig; using LethalConfig.ConfigItems; using LethalConfig.ConfigItems.Options; using Microsoft.CodeAnalysis; using MoreShipUpgrades.Managers; using Newtonsoft.Json; using TMPro; using TeamChaos.UnifiedChaosPack.Config; using TeamChaos.UnifiedChaosPack.NetcodePatcher; using TeamChaos.UnifiedChaosPack.NetworkBehaviors; using TeamChaos.UnifiedChaosPack.PluginManagers; using TeamChaos.UnifiedChaosPack.PluginManagers.MobPlus; using TeamChaos.UnifiedChaosPack.PluginManagers.MobPlus.Coroutines; using TeamChaos.UnifiedChaosPack.PluginManagers.MobPlus.EventBrokers; using TeamChaos.UnifiedChaosPack.PluginManagers.MobPlus.Notifiers; using TeamChaos.UnifiedChaosPack.Schema; using Unity.Netcode; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UIElements; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyCompany("Pirahtays")] [assembly: AssemblyConfiguration("release")] [assembly: AssemblyDescription("Unified Mod Pack / Tweaks created by Pirahtays")] [assembly: AssemblyFileVersion("0.0.1.0")] [assembly: AssemblyInformationalVersion("0.0.1")] [assembly: AssemblyProduct("TeamChaos.UnifiedChaosPack")] [assembly: AssemblyTitle("TeamChaos.UnifiedChaosPack")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.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 TeamChaos.UnifiedChaosPack { public static class GeneralLethalPatches { private static Harmony HarmonyInstance { get; } = new Harmony(Guid.NewGuid().ToString()); private static List<T> FindByName<T>(Func<IEnumerable<T>> selector, params string[] names) where T : MemberInfo { return (from o in selector() where names.Contains(o.Name) select o).ToList(); } public static List<ConstructorInfo> FindConstructorsByName(Type type, params string[] methodNames) { return FindByName(() => AccessTools.GetDeclaredConstructors(type, (bool?)null), methodNames); } public static List<MethodInfo> FindMethodsByName(Type type, params string[] methodNames) { return FindByName(() => AccessTools.GetDeclaredMethods(type), methodNames); } public static List<PropertyInfo> FindPropertiesByName(Type type, params string[] methodNames) { return FindByName(() => AccessTools.GetDeclaredProperties(type), methodNames); } public static void PatchMethods(GeneralConfig config) { } public static bool __AnnoyingLogStopper() { return false; } } public static class PluginPackageInfo { public const string PLUGIN_GUID = "TeamChaos.UnifiedChaosPack"; public const string PLUGIN_NAME = "UnifiedChaosPack"; public const string PLUGIN_VERSION = "0.0.1"; } [BepInPlugin("TeamChaos.UnifiedChaosPack", "UnifiedChaosPack", "0.0.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.*/)] public class UnifiedChaosPlugin : BaseUnityPlugin { private const string DEPENDENCY_CSYNC = "com.sigurd.csync"; private const string DEPENDENCY_LCSOUNDTOOL = "LCSoundTool"; private const string DEPENDENCY_LETHALCONFIG = "ainavt.lc.lethalconfig"; public const string DEPENDENCY_LGU = "com.malco.lethalcompany.moreshipupgrades"; private const string DEPENDENCY_FACILITY_MELTDOWN = "me.loaforc.facilitymeltdown"; private readonly GeneralConfig _config = new GeneralConfig(); private readonly Dictionary<Type, UnifiedChaosPluginManager> _pluginMap = new Dictionary<Type, UnifiedChaosPluginManager>(); private GameObject _gameObject; public AssetBundle UnityAssetBundle { get; } public static GeneralConfig GeneralConfig => Instance._config; public static UnifiedChaosPlugin Instance { get; private set; } public UnifiedChaosPlugin() { Instance = this; UnityAssetBundle = AssetBundle.LoadFromFile(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "unifiedchaospack")); } private void Awake() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown ((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin TeamChaos.UnifiedChaosPack is initializing!"); NetcodePatcher(); _gameObject = new GameObject("TeamChaos.UnifiedChaosPack"); _config.Init(); GeneralLethalPatches.PatchMethods(_config); UnifiedChaosPluginManager<OpenDoorsInSpacePluginManager, OpenDoorsInSpaceConfig>.Create(this); UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Create(this); UnifiedChaosPluginManager<MoreMobSoundsPluginManager, MobPlusPluginManagerConfig>.Create(this); ((Object)_gameObject).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)_gameObject); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin TeamChaos.UnifiedChaosPack is loaded!"); } public T GetPlugin<T>() where T : UnifiedChaosPluginManager { Type typeFromHandle = typeof(T); if (!_pluginMap.ContainsKey(typeFromHandle)) { T val = (((Object)(object)_gameObject != (Object)null) ? _gameObject.GetComponent<T>() : null); if ((Object)(object)val == (Object)null) { return null; } _pluginMap.Add(typeFromHandle, val); } return (T)_pluginMap[typeFromHandle]; } public T RegisterPlugin<T>() where T : UnifiedChaosPluginManager { Type typeFromHandle = typeof(T); if (!_pluginMap.ContainsKey(typeFromHandle)) { _pluginMap.Remove(typeFromHandle); } return _gameObject.AddComponent<T>(); } public void Log(LogLevel logLevel, string callingPlugin, string message, bool verboseOnly = false) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (!verboseOnly || GeneralConfig.IsVerboseLogging) { ((BaseUnityPlugin)this).Logger.Log(logLevel, (object)("[UnifiedChaos::" + callingPlugin + "]:" + message)); } } public void LogDebug(string callingPlugin, string message, bool verboseOnly = false) { Log((LogLevel)32, callingPlugin, message, verboseOnly); } public void LogMessage(string callingPlugin, string message, bool verboseOnly = false) { Log((LogLevel)8, callingPlugin, message, verboseOnly); } public void LogAll(string callingPlugin, string message, bool verboseOnly = false) { Log((LogLevel)63, callingPlugin, message, verboseOnly); } public void LogError(string callingPlugin, string message, bool verboseOnly = false) { Log((LogLevel)2, callingPlugin, message, verboseOnly); } public void LogFatal(string callingPlugin, string message, bool verboseOnly = false) { Log((LogLevel)1, callingPlugin, message, verboseOnly); } public void LogInfo(string callingPlugin, string message, bool verboseOnly = false) { Log((LogLevel)16, callingPlugin, message, verboseOnly); } public void LogNone(string callingPlugin, string message, bool verboseOnly = false) { Log((LogLevel)0, callingPlugin, message, verboseOnly); } public void LogWarning(string callingPlugin, string message, bool verboseOnly = false) { Log((LogLevel)4, callingPlugin, message, verboseOnly); } private void NetcodePatcher() { Type[] types = Assembly.GetExecutingAssembly().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) { object[] customAttributes = methodInfo.GetCustomAttributes(typeof(RuntimeInitializeOnLoadMethodAttribute), inherit: false); if (customAttributes.Length != 0) { Instance.LogDebug(((object)this).GetType().Name, "NetcodePatcher - Invoking " + methodInfo.DeclaringType.Name + "::" + methodInfo.Name + " "); methodInfo.Invoke(null, null); MethodInfo methodInfo2 = AccessTools.FindIncludingBaseTypes<MethodInfo>(methodInfo.DeclaringType, (Func<Type, MethodInfo>)((Type t) => t.GetMethod("Patch"))); if (!(methodInfo2 == null)) { Instance.LogDebug(((object)this).GetType().Name, "NetcodePatcher - Patching " + methodInfo.DeclaringType.Name + "(" + methodInfo2.DeclaringType.Name + ")::" + methodInfo2.Name + " "); methodInfo2.Invoke(null, null); } } } } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "TeamChaos.UnifiedChaosPack"; public const string PLUGIN_NAME = "TeamChaos.UnifiedChaosPack"; public const string PLUGIN_VERSION = "0.0.1"; } } namespace TeamChaos.UnifiedChaosPack.Schema { public class AudioFileInfo { [JsonProperty(PropertyName = "file")] [CanBeNull] public string Filename { get; set; } [JsonProperty(PropertyName = "chance")] public int Chance { get; set; } = 100; [JsonProperty(PropertyName = "is-silence")] public bool IsSilence { get; set; } [JsonProperty(PropertyName = "is-default")] public bool IsDefault { get; set; } } public class AudioMetadata { [JsonRequired] [JsonProperty(PropertyName = "type")] public string Type { get; set; } [JsonRequired] [JsonProperty(PropertyName = "field")] public string Field { get; set; } [JsonProperty(PropertyName = "exclusive")] public bool IsPlayerExclusive { get; set; } [JsonRequired] [JsonProperty(PropertyName = "replacement-file-data")] public List<AudioFileInfo> AudioFileInfo { get; set; } = new List<AudioFileInfo>(); } public class AudioMetadataCollection { private const string BASE_FILENAME = "sound-data"; private readonly Dictionary<ulong, List<PlayerSpecificAudioMetadata>> _playerSpecificData; private List<AudioMetadata> _defaultAudioMetadata; private AudioMetadataCollection() { _defaultAudioMetadata = Enumerable.Empty<AudioMetadata>().ToList(); _playerSpecificData = new Dictionary<ulong, List<PlayerSpecificAudioMetadata>>(); } public void Dump() { UnifiedChaosPlugin.Instance.LogDebug("AudioMetadataCollection", $"Dumping all AudioMetadata - _default:{_defaultAudioMetadata.Count}, _playerSpecific:{_playerSpecificData.SelectMany((KeyValuePair<ulong, List<PlayerSpecificAudioMetadata>> p) => p.Value).Count()}"); IEnumerable<IGrouping<(string, string), (ulong?, AudioMetadata)>> enumerable = from a in _playerSpecificData.SelectMany((KeyValuePair<ulong, List<PlayerSpecificAudioMetadata>> d) => ((IEnumerable<PlayerSpecificAudioMetadata>)d.Value).Select((Func<PlayerSpecificAudioMetadata, (ulong?, AudioMetadata)>)((PlayerSpecificAudioMetadata i) => (d.Key, i)))).Union(((IEnumerable<AudioMetadata>)_defaultAudioMetadata).Select((Func<AudioMetadata, (ulong?, AudioMetadata)>)((AudioMetadata d) => (null, d)))) group a by (a.Value.Field, a.Value.Type); foreach (IGrouping<(string, string), (ulong?, AudioMetadata)> item in enumerable) { UnifiedChaosPlugin.Instance.LogDebug("AudioMetadataCollection", "Audio Metadata " + item.Key.Item2 + "::" + item.Key.Item1); foreach (var item2 in item) { foreach (AudioFileInfo item3 in item2.Item2.AudioFileInfo) { string message = ((!(item2.Item2 is PlayerSpecificAudioMetadata)) ? $"\t Available files: {item3.Filename}[{item3.Chance}] - Default:{item3.IsDefault}, Silent:{item3.IsSilence}" : $"\t Available files[{item2.Item1}]: {item3.Filename}[{item3.Chance}] - Default:{item3.IsDefault}, Silent:{item3.IsSilence}, IsExclusive:{item2.Item2.IsPlayerExclusive}"); UnifiedChaosPlugin.Instance.LogDebug("AudioMetadataCollection", message); } } } } public void Dump<T>(string field, ulong? playerId = null) { List<AudioFileInfo> filteredData = GetFilteredData<T>(field, playerId); UnifiedChaosPlugin.Instance.LogDebug("AudioMetadataCollection", $"Dumping filtered AudioMetadata - _data:{filteredData.Count}"); foreach (AudioFileInfo item in filteredData) { UnifiedChaosPlugin.Instance.LogDebug("AudioMetadataCollection", "Audio Metadata " + typeof(T).Name + "::" + field); string message = $"\t Available files: {item.Filename}[{item.Chance}] - Default:{item.IsDefault}, Silent:{item.IsSilence}"; UnifiedChaosPlugin.Instance.LogDebug("AudioMetadataCollection", message); } } public IEnumerable<string> GetAudioIdsToLoad() { return (from a in _defaultAudioMetadata.SelectMany((AudioMetadata r) => r.AudioFileInfo).Union(_playerSpecificData.Values.SelectMany((List<PlayerSpecificAudioMetadata> r) => r.SelectMany((PlayerSpecificAudioMetadata p) => p.AudioFileInfo))) where !string.IsNullOrWhiteSpace(a.Filename) select a.Filename).Distinct(); } private static List<T> GetAudioMetadataFromFile<T>(string file) where T : AudioMetadata { if (!File.Exists(file)) { throw new FileNotFoundException("Json file not found!", file); } string text = File.ReadAllText(file); return JsonConvert.DeserializeObject<List<T>>(text); } public List<AudioFileInfo> GetFilteredData<T>(string field, ulong? playerId = null) { string type = typeof(T).Name; IEnumerable<AudioFileInfo> enumerable = _defaultAudioMetadata.FirstOrDefault((AudioMetadata a) => a.Field == field && a.Type == type)?.AudioFileInfo; IEnumerable<AudioFileInfo> enumerable2 = enumerable ?? Enumerable.Empty<AudioFileInfo>(); if (playerId.HasValue && _playerSpecificData.ContainsKey(playerId.Value)) { PlayerSpecificAudioMetadata playerSpecificAudioMetadata = _playerSpecificData[playerId.Value].FirstOrDefault((PlayerSpecificAudioMetadata a) => a.Field == field && a.Type == type); enumerable = playerSpecificAudioMetadata?.AudioFileInfo; IEnumerable<AudioFileInfo> enumerable3 = enumerable ?? Enumerable.Empty<AudioFileInfo>(); enumerable2 = ((playerSpecificAudioMetadata != null && playerSpecificAudioMetadata.IsPlayerExclusive) ? enumerable3 : enumerable2.Union(enumerable3)); } return enumerable2.ToList(); } public static AudioMetadataCollection LoadAudioMetadataCollection(string path) { Regex regex = new Regex("sound-data\\.?(\\d+)*\\.json"); DirectoryInfo directoryInfo = new DirectoryInfo(path); IEnumerable<FileInfo> enumerable = directoryInfo.EnumerateFiles("sound-data*.json"); AudioMetadataCollection audioMetadataCollection = new AudioMetadataCollection(); UnifiedChaosPlugin.Instance.LogInfo("AudioMetadataCollection", "[LoadAudioMetadataCollection]: Begin Load from '" + path + "'"); foreach (FileInfo item in enumerable) { UnifiedChaosPlugin.Instance.LogInfo("AudioMetadataCollection", string.Format("[{0}]: Loading file: '{1}'", "LoadAudioMetadataCollection", item)); if (string.Equals(item.Name, "sound-data.json", StringComparison.InvariantCultureIgnoreCase)) { audioMetadataCollection._defaultAudioMetadata = GetAudioMetadataFromFile<AudioMetadata>(item.FullName); continue; } Group group = regex.Match(item.Name.ToLower())?.Groups[1]; if (group != null && group.Success && ulong.TryParse(group.Value, out var result)) { List<PlayerSpecificAudioMetadata> audioMetadataFromFile = GetAudioMetadataFromFile<PlayerSpecificAudioMetadata>(item.FullName); audioMetadataCollection._playerSpecificData.Add(result, audioMetadataFromFile); } } if (audioMetadataCollection._defaultAudioMetadata.SelectMany((AudioMetadata r) => r.AudioFileInfo).Union(audioMetadataCollection._playerSpecificData.Values.SelectMany((List<PlayerSpecificAudioMetadata> r) => r.SelectMany((PlayerSpecificAudioMetadata p) => p.AudioFileInfo))).Any((AudioFileInfo a) => string.IsNullOrWhiteSpace(a.Filename) && !a.IsSilence && !a.IsDefault)) { UnifiedChaosPlugin.Instance.LogError("AudioMetadataCollection", "Audio Manifest has empty filenames for records not flagged as default or silence!"); } return audioMetadataCollection; } } public class AudioPatchMetadata : AudioMetadata { [JsonRequired] [JsonProperty(PropertyName = "method")] public string Method { get; set; } [JsonRequired] [JsonProperty(PropertyName = "patch-method")] public string PatchMethod { get; set; } } public class PlayerSpecificAudioMetadata : AudioMetadata { [JsonProperty(PropertyName = "exclusive")] public new bool IsPlayerExclusive { get; set; } } } namespace TeamChaos.UnifiedChaosPack.PluginManagers { public class MoreMobSoundsPluginManager : UnifiedChaosPluginManager<MoreMobSoundsPluginManager, MobPlusPluginManagerConfig> { private const string PATCH_DATA_FILE = "sound-data.json"; private readonly Dictionary<Type, MobPlusPatcher> _patchers = new Dictionary<Type, MobPlusPatcher>(); private readonly Dictionary<Type, EventBroker> _patchersNew = new Dictionary<Type, EventBroker>(); private AudioMetadataCollection _audioMetadata; protected override string PluginFriendlyName => "MobPlus"; private EnemyAI GetNetworkEnemyInstance(Type type, ulong id) { if (!_patchersNew.TryGetValue(type, out var value)) { return null; } return value?.GetEnemyInstanceByNetworkId(id); } private void HandlePlayAudioEvent(RandomAudioSyncNetworkHandler.AudioEventArgs e) { LogWarning(string.Format("[{0}]: {1}:'{2}', {3}:'{4}', {5}:'{6}'", "HandlePlayAudioEvent", "SourceType", e.SourceType, "SourceID", e.SourceID, "AudioKey", e.AudioKey)); EnemyAI networkEnemyInstance = GetNetworkEnemyInstance(e.SourceType, e.SourceID); if (!Object.op_Implicit((Object)(object)networkEnemyInstance)) { LogError(string.Format("[{0}]: Enemy instance not found! ({1}:{2})", "HandlePlayAudioEvent", e.SourceType, e.SourceID)); } else if (!e.Fallback) { HandlePlayAudioEvent(networkEnemyInstance, e.AudioKey); } } private void HandlePlayAudioEvent(EnemyAI ai, string audioKey) { bool flag = !string.IsNullOrWhiteSpace(audioKey); AudioClip preloadedAudioClip; if (!flag || !Object.op_Implicit((Object)(object)(preloadedAudioClip = GetPreloadedAudioClip(audioKey)))) { string message = (flag ? ("[HandlePlayAudioEvent]: Audioclip not found/loaded! (" + ((object)ai).GetType().Name + ":" + audioKey + ")") : "[HandlePlayAudioEvent]: Audiokey is null or blank!"); LogInfo(message); } else { ((MonoBehaviour)this).StartCoroutine(PlayAndWait(ai.creatureVoice, preloadedAudioClip)); } } private IEnumerator PlayAndWait(AudioSource audioSource, AudioClip clip) { AudioSource.PlayOneShotHelper(audioSource, clip, 1f); yield return (object)new WaitForSeconds(clip.length); } private void PlayOneShot<T>(T ai, string field, Action<T> fallback = null, ulong? playerId = null) where T : EnemyAI { UnifiedChaosPluginNetworkHandler<RandomAudioSyncNetworkHandler>.Instance.PlayRandomOneShot<T>(ai, field, _audioMetadata, (Action<string>)delegate(string audioKey) { HandlePlayAudioEvent((EnemyAI)(object)ai, audioKey); }, fallback, playerId); } public override void Start() { _config.Init(); LogInfo(((object)this).GetType().Name + " Start"); _audioMetadata = AudioMetadataCollection.LoadAudioMetadataCollection(base.FullSoundPathForPlugin); _audioMetadata.Dump(); LoadAudio(_audioMetadata.GetAudioIdsToLoad()); _patchersNew.InitEventBroker(EventBroker<HoarderBugAINotifier, HoarderBugAI, HoarderBugEventBroker>.GetInstance(), InitHoarderBugPatches).InitEventBroker(EventBroker<FlowermanAINotifier, FlowermanAI, FlowermanEventBroker>.GetInstance(), InitFlowermanPatches).InitEventBroker(EventBroker<SpringManAINotifier, SpringManAI, SpringManEventBroker>.GetInstance(), InitSpringManPatches); UnifiedChaosPluginNetworkHandler<RandomAudioSyncNetworkHandler>.NetworkSpawn += delegate { UnifiedChaosPluginManager<MoreMobSoundsPluginManager, MobPlusPluginManagerConfig>.Instance.LogError("Called: NetworkSpawn"); RandomAudioSyncNetworkHandler.PlayAudioEvent += UnifiedChaosPluginManager<MoreMobSoundsPluginManager, MobPlusPluginManagerConfig>.Instance.HandlePlayAudioEvent; }; UnifiedChaosPluginNetworkHandler<RandomAudioSyncNetworkHandler>.NetworkDespawn += delegate { UnifiedChaosPluginManager<MoreMobSoundsPluginManager, MobPlusPluginManagerConfig>.Instance.LogError("Called: NetworkDespawn"); RandomAudioSyncNetworkHandler.PlayAudioEvent -= UnifiedChaosPluginManager<MoreMobSoundsPluginManager, MobPlusPluginManagerConfig>.Instance.HandlePlayAudioEvent; }; } private void InitBaboonBirdPatches(BaboonBirdExtender obj) { } private void InitBlobPatches(BlobExtender obj) { } private void InitCentipedePatches(CentipedeExtender obj) { } private void InitCrawlerPatches(CrawlerExtender obj) { } private void InitDocileLocustBeesPatches(DocileLocustBeesExtender obj) { } private void InitDoublewingPatches(DoublewingExtender obj) { } private void InitDressGirlPatches(DressGirlExtender obj) { } public void InitFlowermanPatches(FlowermanEventBroker broker) { broker.AngryAtPlayer += delegate(FlowermanAI ai, PlayerControllerB player) { PlayOneShot<FlowermanAI>(ai, "AngryAtPlayer", (Action<FlowermanAI>)null, player?.playerSteamId); }; broker.DroppingBody += delegate(FlowermanAI ai, bool isHome) { PlayOneShot<FlowermanAI>(ai, isHome ? "DroppingBodyAtHome" : "DroppingBody", (Action<FlowermanAI>)null, (ulong?)null); }; broker.EvadingPlayer += delegate(FlowermanAI ai, PlayerControllerB player) { PlayOneShot<FlowermanAI>(ai, "EvadingPlayer", (Action<FlowermanAI>)null, player?.playerSteamId); }; broker.KilledPlayer += delegate(FlowermanAI ai, bool carryingBody, PlayerControllerB player) { PlayOneShot<FlowermanAI>(ai, carryingBody ? "KilledPlayerCarryingBody" : "KilledPlayer", (Action<FlowermanAI>)null, player?.playerSteamId); }; broker.StaringDownPlayer += delegate(FlowermanAI ai, PlayerControllerB player) { PlayOneShot<FlowermanAI>(ai, "StaringDownPlayer", (Action<FlowermanAI>)null, player?.playerSteamId); }; } private void InitForestGiantPatches(ForestGiantExtender obj) { } private void InitHoarderBugPatches(HoarderBugEventBroker broker) { broker.AngryAtPlayer += delegate(HoarderBugAI ai, PlayerControllerB player) { PlayOneShot<HoarderBugAI>(ai, "AngryAtPlayer", (Action<HoarderBugAI>)null, player?.playerSteamId); }; broker.ChangedWatchingPlayer += delegate(HoarderBugAI ai, PlayerControllerB player) { PlayOneShot<HoarderBugAI>(ai, "ChangedWatchingPlayer", (Action<HoarderBugAI>)delegate(HoarderBugAI bugAI) { RoundManager.PlayRandomClip(((EnemyAI)bugAI).creatureVoice, bugAI.chitterSFX, true, 1f, 0, 1000); }, player?.playerSteamId); }; broker.ItemDroppedInNest += delegate(HoarderBugAI ai, HoarderBugItem item) { PlayOneShot<HoarderBugAI>(ai, "ItemDroppedInNest", (Action<HoarderBugAI>)null, (ulong?)null); }; broker.HuntingItem += delegate(HoarderBugAI ai, GrabbableObject item) { PlayOneShot<HoarderBugAI>(ai, "HuntingItem", (Action<HoarderBugAI>)null, (ulong?)null); }; broker.ItemGrabbed += delegate(HoarderBugAI ai, HoarderBugItem item) { PlayOneShot<HoarderBugAI>(ai, "ItemGrabbed", (Action<HoarderBugAI>)null, (ulong?)null); }; } private void InitJesterPatches(JesterExtender obj) { } private void InitLassoManPatches(LassoManExtender obj) { } private void InitMaskedPlayerEnemyPatches(MaskedPlayerEnemyExtender obj) { } private void InitMouthDogPatches(MouthDogExtender obj) { } private void InitNutcrackerEnemyPatches(NutcrackerEnemyExtender obj) { } private void InitPufferAIPatches(PufferExtender obj) { } private void InitRedLocustBeesPatches(RedLocustBeesExtender obj) { } private void InitSandSpiderPatches(SandSpiderExtender obj) { } private void InitSandWormPatches(SandWormExtender obj) { } private void InitSpringManPatches(SpringManEventBroker broker) { broker.StaringAtPlayer += delegate(SpringManAI ai, PlayerControllerB player) { PlayOneShot<SpringManAI>(ai, "StaringAtPlayer", (Action<SpringManAI>)null, player?.playerSteamId); }; broker.StaringAtPlayerForAWhile += delegate(SpringManAI ai, PlayerControllerB player) { PlayOneShot<SpringManAI>(ai, "StaringAtPlayerForAWhile", (Action<SpringManAI>)null, player?.playerSteamId); }; broker.StaringAtPlayerForALongTime += delegate(SpringManAI ai, PlayerControllerB player) { PlayOneShot<SpringManAI>(ai, "StaringAtPlayerForALongTime", (Action<SpringManAI>)null, player?.playerSteamId); }; } } public class MultiSoundReplacePluginManager : UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig> { private const string PATCH_DATA_FILE = "soundpatch.json"; protected List<AudioPatchMetadata> _replacementData = new List<AudioPatchMetadata>(); protected override string PluginFriendlyName => "MultiSoundReplacer"; public override void Start() { _config.Init(); LogInfo(((object)this).GetType().Name + " Start"); LoadDynamicPatchData(); LoadAudio(GetAudioIdsToLoad()); DynamicPatch(); } private void LoadDynamicPatchData() { string path = Path.Combine(base.FullSoundPathForPlugin, "soundpatch.json"); if (File.Exists(path)) { string text = File.ReadAllText(path); _replacementData = JsonConvert.DeserializeObject<List<AudioPatchMetadata>>(text); } } private void DynamicPatch() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Expected O, but got Unknown string contents = JsonConvert.SerializeObject((object)_replacementData, (Formatting)1); File.WriteAllText(Path.Combine(base.FullSoundPathForPlugin, "debug-test.json"), contents); Harmony val = new Harmony(Guid.NewGuid().ToString()); foreach (var item2 in GetMethodsToPatch()) { string item = item2.PatchMethod; if (!(item == "FacilityMeltdownAssetReplace")) { if (item == "StaticAudioClipArrayReplace") { StaticAudioClipArrayReplace(item2); continue; } UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.LogInfo("MultiSoundReplacePluginManager::DynamicPatch - Patching " + item2.Type + "::" + item2.Method + " with " + item2.PatchMethod); Type type = AccessTools.TypeByName(item2.Type); MethodInfo methodInfo = AccessTools.Method(type, item2.Method, (Type[])null, (Type[])null); MethodInfo method = ((object)this).GetType().GetMethod(item2.PatchMethod); val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.LogInfo("MultiSoundReplacePluginManager::DynamicPatch - Patching " + item2.Type + "::" + item2.Method + " with " + item2.PatchMethod); FacilityMeltdownAssetReplace(item2); } } } public static void TestPatch(ref object __instance, MethodBase __originalMethod) { } private IEnumerable<(string Type, string Method, string PatchMethod)> GetMethodsToPatch() { return _replacementData.Select((AudioPatchMetadata r) => (r.Type, r.Method, r.PatchMethod)).Distinct(); } private IEnumerable<AudioPatchMetadata> GetAudioPatchesForMethod(string type, string method, [CallerMemberName] string patchMethod = null) { return _replacementData.Where((AudioPatchMetadata r) => r.PatchMethod == patchMethod && r.Type == type && r.Method == method); } private IEnumerable<string> GetAudioIdsToLoad() { return _replacementData.SelectMany((AudioPatchMetadata r) => r.AudioFileInfo.Select((AudioFileInfo a) => a.Filename)).Distinct(); } public static void FacilityMeltdownAssetReplace((string Type, string Method, string PatchMethod) methodInfo) { Type type = AccessTools.TypeByName(methodInfo.Type); if (type == null) { UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.LogError("'" + methodInfo.Type + "' was not found - mod may not be installed!"); return; } MeltdownPlugin val = AccessTools.StaticFieldRefAccess<MeltdownPlugin>(type, "instance"); if ((Object)(object)val == (Object)null) { UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.LogError("'" + methodInfo.Type + "' instance was not found - mod may not be installed!"); return; } PropertyInfo propertyInfo = AccessTools.Property(type, "assets"); object value = propertyInfo.GetValue(val); foreach (AudioPatchMetadata item in UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.GetAudioPatchesForMethod(methodInfo.Type, methodInfo.Method, "FacilityMeltdownAssetReplace")) { UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.LogWarning("Implementing Patches on " + type.Name + "::" + item.Method); Traverse val2 = Traverse.Create(value).Property(methodInfo.Method, (object[])null); if (item.AudioFileInfo.Count == 1) { AudioFileInfo audioFileInfo = item.AudioFileInfo.Single(); val2.SetValue((object)UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.GetPreloadedAudioClips(audioFileInfo.Filename, val2.GetValue<AudioClip[]>())); continue; } AudioClip preloadedAudioClip = UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.GetPreloadedAudioClip(item.AudioFileInfo.OrderByDescending((AudioFileInfo a) => a.Chance).First().Filename); val2.SetValue((object)new AudioClip[1] { preloadedAudioClip }); foreach (AudioFileInfo item2 in item.AudioFileInfo) { SoundTool.ReplaceAudioClip(preloadedAudioClip, UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.GetPreloadedAudioClip(item2.Filename), (float)item2.Chance / 100f); } } } public static void StaticAudioClipArrayReplace((string Type, string Method, string PatchMethod) methodInfo) { UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.LogInfo("MultiSoundReplacePluginManager::StaticAudioClipArrayReplace - Patching " + methodInfo.Type + "::" + methodInfo.Method); Type type = AccessTools.TypeByName(methodInfo.Type); if (type == null) { UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.LogError("'" + methodInfo.Type + "' was not found - mod may not be installed!"); return; } foreach (AudioPatchMetadata item in UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.GetAudioPatchesForMethod(methodInfo.Type, string.Empty, "StaticAudioClipArrayReplace")) { UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.LogWarning("Implementing Patches on " + type.Name + "::" + item.Field); FieldInfo fieldInfo = AccessTools.Field(type, item.Field); if (fieldInfo == null) { UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.LogError("'" + type.Name + "." + item.Field + "' was not found - mod may be incompatible or not found!"); break; } if (fieldInfo.FieldType != typeof(AudioClip[])) { UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.LogError("'" + type.Name + "." + item.Field + "' is not of type AudioClip[]!"); break; } AudioClip[] original = AccessTools.StaticFieldRefAccess<AudioClip[]>(type, item.Field); Traverse val = Traverse.Create(type).Field(item.Field); if (item.AudioFileInfo.Count == 1) { AudioFileInfo audioFileInfo = item.AudioFileInfo.Single(); val.SetValue((object)UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.GetPreloadedAudioClips(audioFileInfo.Filename, original)); continue; } AudioClip preloadedAudioClip = UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.GetPreloadedAudioClip(item.AudioFileInfo.OrderByDescending((AudioFileInfo a) => a.Chance).First().Filename); val.SetValue((object)new AudioClip[1] { preloadedAudioClip }); foreach (AudioFileInfo item2 in item.AudioFileInfo) { SoundTool.ReplaceAudioClip(preloadedAudioClip, UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.GetPreloadedAudioClip(item2.Filename), (float)item2.Chance / 100f); } } } public static void InstanceAudioClipArrayPatch(ref object __instance, MethodBase __originalMethod) { Type type = __instance.GetType(); UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.LogInfo("MultiSoundReplacePluginManager::InstanceAudioClipArrayPatch - Patching " + type.Name + "::" + __originalMethod.Name); foreach (AudioPatchMetadata item in UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.GetAudioPatchesForMethod(type.Name, __originalMethod.Name, "InstanceAudioClipArrayPatch")) { FieldInfo fieldInfo = AccessTools.Field(type, item.Field); if (fieldInfo == null) { UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.LogError("'" + type.Name + "." + item.Field + "' was not found!"); break; } if (fieldInfo.FieldType != typeof(AudioClip[])) { UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.LogError("'" + type.Name + "." + item.Field + "' is not of type AudioClip[]!"); break; } AudioClip[] original = fieldInfo.GetValue(__instance) as AudioClip[]; if (item.AudioFileInfo.Count == 1) { AudioFileInfo audioFileInfo = item.AudioFileInfo.Single(); fieldInfo.SetValue(__instance, UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.GetPreloadedAudioClips(audioFileInfo.Filename, original)); continue; } AudioClip preloadedAudioClip = UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.GetPreloadedAudioClip(item.AudioFileInfo.OrderByDescending((AudioFileInfo a) => a.Chance).First().Filename); fieldInfo.SetValue(__instance, new AudioClip[1] { preloadedAudioClip }); foreach (AudioFileInfo item2 in item.AudioFileInfo) { SoundTool.ReplaceAudioClip(preloadedAudioClip, UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.GetPreloadedAudioClip(item2.Filename), (float)item2.Chance / 100f); } } } public static void InstanceAudioClipPatch(ref object __instance, MethodBase __originalMethod) { Type type = __instance.GetType(); UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.LogInfo("MultiSoundReplacePluginManager::InstanceAudioClipPatch - Patching " + type.Name + "::" + __originalMethod.Name); foreach (AudioPatchMetadata item in UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.GetAudioPatchesForMethod(type.Name, __originalMethod.Name, "InstanceAudioClipPatch")) { FieldInfo fieldInfo = AccessTools.Field(type, item.Field); if (fieldInfo == null) { UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.LogError("'" + type.Name + "." + item.Field + "' was not found!"); break; } if (fieldInfo.FieldType != typeof(AudioClip)) { UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.LogError("'" + type.Name + "." + item.Field + "' is not of type AudioClip!"); break; } object? value = fieldInfo.GetValue(__instance); AudioClip val = (AudioClip)((value is AudioClip) ? value : null); if (item.AudioFileInfo.Count == 1) { AudioFileInfo audioFileInfo = item.AudioFileInfo.Single(); fieldInfo.SetValue(__instance, UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.GetPreloadedAudioClip(audioFileInfo.Filename) ?? val); continue; } AudioClip preloadedAudioClip = UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.GetPreloadedAudioClip(item.AudioFileInfo.OrderByDescending((AudioFileInfo a) => a.Chance).First().Filename); fieldInfo.SetValue(__instance, preloadedAudioClip); foreach (AudioFileInfo item2 in item.AudioFileInfo) { SoundTool.ReplaceAudioClip(preloadedAudioClip, UnifiedChaosPluginManager<MultiSoundReplacePluginManager, MultiSoundReplaceConfig>.Instance.GetPreloadedAudioClip(item2.Filename), (float)item2.Chance / 100f); } } } } public class OpenDoorsInSpacePluginManager : UnifiedChaosPluginManager<OpenDoorsInSpacePluginManager, OpenDoorsInSpaceConfig> { private class FiredMessagePatcher { private const string ELEMENT_TEXT = "MaskImage/HeaderText"; private const string ELEMENT_SUBTEXT = "MaskImage/HeaderText (1)"; private TextMeshProUGUI _youAreFiredDueToNegligenceSubtext; private TextMeshProUGUI _youAreFiredDueToNegligenceText; private GameObject _youAreFiredSubtext; private GameObject _youAreFiredText; private GameObject FindOriginalGameOverElement(string element) { return GameObject.Find("/Systems/UI/Canvas/GameOverScreen/" + element); } public void Init(bool force = false) { if ((Object)(object)_youAreFiredText == (Object)null) { _youAreFiredText = FindOriginalGameOverElement("MaskImage/HeaderText"); } if ((Object)(object)_youAreFiredSubtext == (Object)null) { _youAreFiredSubtext = FindOriginalGameOverElement("MaskImage/HeaderText (1)"); } if (force && (Object)(object)_youAreFiredDueToNegligenceText != (Object)null) { _youAreFiredDueToNegligenceText = null; } if ((Object)(object)_youAreFiredDueToNegligenceText == (Object)null) { _youAreFiredDueToNegligenceText = ReplaceTextElement("MaskImage/HeaderText", "(OpenDoorsInSpace fired text)", UnifiedChaosPluginManager<OpenDoorsInSpacePluginManager, OpenDoorsInSpaceConfig>.Instance._config.EjectedMessageText); } if (force && (Object)(object)_youAreFiredDueToNegligenceSubtext != (Object)null) { _youAreFiredDueToNegligenceSubtext = null; } if ((Object)(object)_youAreFiredDueToNegligenceSubtext == (Object)null) { _youAreFiredDueToNegligenceSubtext = ReplaceTextElement("MaskImage/HeaderText (1)", "(OpenDoorsInSpace fired subtext)", UnifiedChaosPluginManager<OpenDoorsInSpacePluginManager, OpenDoorsInSpaceConfig>.Instance._config.EjectedMessageSubtext); } } public void Activate() { _youAreFiredText.gameObject.SetActive(false); ((Component)_youAreFiredDueToNegligenceText).gameObject.SetActive(true); _youAreFiredSubtext.gameObject.SetActive(false); ((Component)_youAreFiredDueToNegligenceSubtext).gameObject.SetActive(true); } public IEnumerator Deactivate() { yield return (object)new WaitForSeconds(1f); GameObject youAreFiredText = _youAreFiredText; if (youAreFiredText != null) { GameObject gameObject = youAreFiredText.gameObject; if (gameObject != null) { gameObject.SetActive(true); } } TextMeshProUGUI youAreFiredDueToNegligenceText = _youAreFiredDueToNegligenceText; if (youAreFiredDueToNegligenceText != null) { GameObject gameObject2 = ((Component)youAreFiredDueToNegligenceText).gameObject; if (gameObject2 != null) { gameObject2.SetActive(false); } } GameObject youAreFiredSubtext = _youAreFiredSubtext; if (youAreFiredSubtext != null) { GameObject gameObject3 = youAreFiredSubtext.gameObject; if (gameObject3 != null) { gameObject3.SetActive(true); } } TextMeshProUGUI youAreFiredDueToNegligenceSubtext = _youAreFiredDueToNegligenceSubtext; if (youAreFiredDueToNegligenceSubtext != null) { GameObject gameObject4 = ((Component)youAreFiredDueToNegligenceSubtext).gameObject; if (gameObject4 != null) { gameObject4.SetActive(false); } } } private TextMeshProUGUI ReplaceTextElement(string element, string suffix, string text) { GameObject val = FindOriginalGameOverElement(element); GameObject val2 = Object.Instantiate<GameObject>(val, val.transform.parent); ((Object)val2).name = ((Object)val2).name + " " + suffix; val2.SetActive(false); TextMeshProUGUI component = val2.GetComponent<TextMeshProUGUI>(); ((TMP_Text)component).text = text; return component; } } public class LguPatches { [HarmonyPatch(typeof(LguStore), "PlayersFiredServerRpc")] [HarmonyPrefix] public static bool LguStore_PlayersFiredServerRpc_Prefix() { return !UnifiedChaosPluginManager<OpenDoorsInSpacePluginManager, OpenDoorsInSpaceConfig>.Instance.IsEjectingDueToNegligence; } } private const string SOUND_DOORWARN = "door-open-warning"; private const string SOUND_DOORERROR = "door-error"; private const string SOUND_AIRBLAST = "fartwithreverb"; private AudioSource _airBlastAudioSource; private FiredMessagePatcher _firedMessagePatcher; public bool IsEjectingDueToNegligence { get; internal set; } protected override string PluginFriendlyName => "OpenDoorsInSpace"; public override void Start() { _config.Init(); _config.ConfigValueUpdated += ConfigValueUpdated; LogInfo(((object)this).GetType().Name + " Start"); SceneManager.activeSceneChanged += HandleActiveSceneChanged; _airBlastAudioSource = ((Component)this).gameObject.AddComponent<AudioSource>(); _airBlastAudioSource.loop = false; _airBlastAudioSource.spatialBlend = 0f; _firedMessagePatcher = new FiredMessagePatcher(); LoadAudio(new string[3] { "door-open-warning", "fartwithreverb", "door-error" }); UnifiedChaosPluginNetworkHandler<OpenDoorsInSpaceNetworkHandler>.NetworkSpawn += delegate { UnifiedChaosPluginManager<OpenDoorsInSpacePluginManager, OpenDoorsInSpaceConfig>.Instance._firedMessagePatcher.Init(); OpenDoorsInSpaceNetworkHandler.OpenDoorEvent += UnifiedChaosPluginManager<OpenDoorsInSpacePluginManager, OpenDoorsInSpaceConfig>.Instance.HandleOpenDoorEvent; }; UnifiedChaosPluginNetworkHandler<OpenDoorsInSpaceNetworkHandler>.NetworkDespawn += delegate { OpenDoorsInSpaceNetworkHandler.OpenDoorEvent -= UnifiedChaosPluginManager<OpenDoorsInSpacePluginManager, OpenDoorsInSpaceConfig>.Instance.HandleOpenDoorEvent; }; } private void ConfigValueUpdated(object sender, string configKey) { } private void HandleActiveSceneChanged(Scene prevScene, Scene activeScene) { ResetEjectingDueToNegligance(); } public void PressDoorOpenServerRPC() { if ((NetworkManager.Singleton.IsHost || NetworkManager.Singleton.IsServer) && !StartOfRound.Instance.firingPlayersCutsceneRunning && !IsEjectingDueToNegligence) { if (!_config.IsEnabled || Random.Range(0f, 100f) > _config.DoorOpenPercent) { UnifiedChaosPluginNetworkHandler<OpenDoorsInSpaceNetworkHandler>.Instance.DoorOpenErrorServerRPC(); } else if (StartOfRound.Instance.firingPlayersCutsceneRunning || IsEjectingDueToNegligence) { LogInfo("Could not eject due to negligence: cutscene is already running"); } else { UnifiedChaosPluginNetworkHandler<OpenDoorsInSpaceNetworkHandler>.Instance.EjectDueToNegligenceServerRPC(); } } } private IEnumerator PlayDoorError() { if (_config.PlayDoorError) { AudioClip preloadedAudioClip = GetPreloadedAudioClip("door-error"); if (!((Object)(object)preloadedAudioClip == (Object)null)) { StartOfRound.Instance.shipDoorAudioSource.PlayOneShot(preloadedAudioClip, _config.DoorErrorVolume); yield return (object)new WaitForSeconds(preloadedAudioClip.length); } } } private IEnumerator PlayDoorOpenWarning() { IsEjectingDueToNegligence = true; StartOfRound shipManager = StartOfRound.Instance; AudioClip preloadedAudioClip = GetPreloadedAudioClip("door-open-warning"); if ((Object)(object)preloadedAudioClip != (Object)null) { StartOfRound.Instance.shipDoorAudioSource.PlayOneShot(preloadedAudioClip, 1f); yield return (object)new WaitForSeconds(preloadedAudioClip.length); } yield return (object)new WaitForSeconds(0.1f); _firedMessagePatcher.Activate(); if (NetworkManager.Singleton.IsHost || NetworkManager.Singleton.IsServer) { shipManager.ManuallyEjectPlayersServerRpc(); } } public void ResetEjectingDueToNegligance() { LogInfo("Reseting ..."); if ((Object)(object)StartOfRound.Instance != (Object)null) { ((MonoBehaviour)this).StartCoroutine(_firedMessagePatcher.Deactivate()); } IsEjectingDueToNegligence = false; } public IEnumerator PlayersEjected(bool abridgedVersion) { AudioClip airBlast = GetPreloadedAudioClip("fartwithreverb"); StartOfRound startOfRound = StartOfRound.Instance; startOfRound.shipDoorsAnimator.SetBool("OpenInOrbit", true); startOfRound.shipDoorAudioSource.PlayOneShot(startOfRound.airPressureSFX); startOfRound.starSphereObject.SetActive(true); startOfRound.starSphereObject.transform.position = ((Component)GameNetworkManager.Instance.localPlayerController).transform.position; yield return (object)new WaitForSeconds(0.25f); startOfRound.suckingPlayersOutOfShip = true; startOfRound.suckingFurnitureOutOfShip = true; PlaceableShipObject[] array = Object.FindObjectsOfType<PlaceableShipObject>(); for (int i = 0; i < array.Length; i++) { if ((Object)(object)array[i].parentObject == (Object)null) { Debug.Log((object)("Error! No parentObject for placeable object: " + startOfRound.unlockablesList.unlockables[array[i].unlockableID].unlockableName)); } array[i].parentObject.StartSuckingOutOfShip(); if (startOfRound.unlockablesList.unlockables[array[i].unlockableID].spawnPrefab) { Collider[] componentsInChildren = ((Component)array[i].parentObject).GetComponentsInChildren<Collider>(); foreach (Collider val in componentsInChildren) { val.enabled = false; } } } GameNetworkManager.Instance.localPlayerController.inSpecialInteractAnimation = true; GameNetworkManager.Instance.localPlayerController.DropAllHeldItems(true, false); HUDManager.Instance.UIAudio.PlayOneShot(startOfRound.suckedIntoSpaceSFX); yield return (object)new WaitForSeconds(6f); SoundManager.Instance.SetDiageticMixerSnapshot(3, 2f); HUDManager.Instance.ShowPlayersFiredScreen(true); _airBlastAudioSource.PlayOneShot(airBlast, 1f); yield return (object)new WaitForSeconds(2f); startOfRound.starSphereObject.SetActive(false); startOfRound.shipDoorAudioSource.Stop(); startOfRound.speakerAudioSource.Stop(); _airBlastAudioSource.Stop(); startOfRound.suckingFurnitureOutOfShip = false; yield return (object)new WaitForSeconds(4f); GameNetworkManager.Instance.localPlayerController.TeleportPlayer(startOfRound.playerSpawnPositions[GameNetworkManager.Instance.localPlayerController.playerClientId].position, false, 0f, false, true); startOfRound.shipDoorsAnimator.SetBool("OpenInOrbit", false); startOfRound.currentPlanetPrefab.transform.position = ((Component)startOfRound.planetContainer).transform.position; startOfRound.suckingPlayersOutOfShip = false; startOfRound.choseRandomFlyDirForPlayer = false; startOfRound.suckingPower = 0f; startOfRound.shipRoomLights.SetShipLightsOnLocalClientOnly(true); yield return (object)new WaitForSeconds(2f); if (((NetworkBehaviour)startOfRound).IsServer) { startOfRound.playersRevived++; startOfRound.playersRevived = 0; startOfRound.EndPlayersFiredSequenceClientRpc(); } else { startOfRound.PlayerHasRevivedServerRpc(); } } public override void Patch() { AddPatchForSoftDependency("com.malco.lethalcompany.moreshipupgrades", typeof(LguPatches)); } private void HandleOpenDoorEvent(OpenDoorsInSpaceNetworkHandler.NetworkEvent networkEvent) { switch (networkEvent) { case OpenDoorsInSpaceNetworkHandler.NetworkEvent.DoorOpenPressed: PressDoorOpenServerRPC(); break; case OpenDoorsInSpaceNetworkHandler.NetworkEvent.DoorActivatedPlayError: ((MonoBehaviour)this).StartCoroutine(PlayDoorError()); break; case OpenDoorsInSpaceNetworkHandler.NetworkEvent.DoorActivatedEjectPlayers: ((MonoBehaviour)this).StartCoroutine(PlayDoorOpenWarning()); break; } } [HarmonyPatch(typeof(HangarShipDoor), "Update")] [HarmonyPostfix] public static void HangarShipDoor_Update_Postfix(HangarShipDoor __instance) { if (!__instance.triggerScript.interactable && StartOfRound.Instance.inShipPhase) { UnifiedChaosPluginManager<OpenDoorsInSpacePluginManager, OpenDoorsInSpaceConfig>.Instance.LogInfo("Forcing door buttons to be interactable"); __instance.triggerScript.interactable = true; } } [HarmonyPatch(typeof(HangarShipDoor), "PlayDoorAnimation")] [HarmonyPrefix] public static bool HangarShipDoor_PlayDoorAnimation_Prefix(bool closed) { if (closed || !StartOfRound.Instance.inShipPhase) { return true; } UnifiedChaosPluginNetworkHandler<OpenDoorsInSpaceNetworkHandler>.Instance.PressDoorOpenServerRPC(); return true; } [HarmonyPatch(typeof(StartOfRound), "ResetShip")] [HarmonyPrefix] public static bool StartOfRound_ResetShip_Prefix() { return !UnifiedChaosPluginManager<OpenDoorsInSpacePluginManager, OpenDoorsInSpaceConfig>.Instance.IsEjectingDueToNegligence; } [HarmonyPatch(typeof(StartOfRound), "PlayFirstDayShipAnimation")] [HarmonyPrefix] public static bool StartOfRound_PlayFirstDayShipAnimation_Prefix() { if (!UnifiedChaosPluginManager<OpenDoorsInSpacePluginManager, OpenDoorsInSpaceConfig>.Instance.IsEjectingDueToNegligence) { return true; } UnifiedChaosPluginManager<OpenDoorsInSpacePluginManager, OpenDoorsInSpaceConfig>.Instance.ResetEjectingDueToNegligance(); return false; } [HarmonyPatch(typeof(StartOfRound), "playersFiredGameOver")] [HarmonyPrefix] public static bool StartOfRound_playersFiredGameOver_Enumerator_Prefix(bool abridgedVersion, ref IEnumerator __result) { if (!UnifiedChaosPluginManager<OpenDoorsInSpacePluginManager, OpenDoorsInSpaceConfig>.Instance.IsEjectingDueToNegligence) { return true; } __result = UnifiedChaosPluginManager<OpenDoorsInSpacePluginManager, OpenDoorsInSpaceConfig>.Instance.PlayersEjected(abridgedVersion); return false; } } public abstract class UnifiedChaosPluginManager<TPlugin, TConfig> : UnifiedChaosPluginManager where TPlugin : UnifiedChaosPluginManager<TPlugin, TConfig> where TConfig : new() { protected TConfig _config = new TConfig(); public static TPlugin Instance => UnifiedChaosPlugin.Instance.GetPlugin<TPlugin>(); public static void Create(UnifiedChaosPlugin basePlugin) { UnifiedChaosPluginManager.Log(basePlugin, (LogLevel)16, "UnifiedChaosPlugin", $"Registering instance of \"{typeof(TPlugin)}\""); TPlugin val = basePlugin.RegisterPlugin<TPlugin>(); val._basePlugin = basePlugin; Harmony.CreateAndPatchAll(((object)val).GetType(), (string)null); val.Patch(); } public virtual void Patch() { } protected void AddPatchForSoftDependency(string key, Type patches) { if (Chainloader.PluginInfos.ContainsKey(key)) { LogInfo("Found '" + Chainloader.PluginInfos[key].Metadata.Name + "' - Adding Patches"); Harmony.CreateAndPatchAll(patches, (string)null); } } } public abstract class UnifiedChaosPluginManager : MonoBehaviour { private readonly Dictionary<string, List<AudioClip>> _audioClips = new Dictionary<string, List<AudioClip>>(); protected UnifiedChaosPlugin _basePlugin; protected abstract string PluginFriendlyName { get; } protected string PluginDir => Path.GetRelativePath(Paths.PluginPath, Path.GetDirectoryName(((BaseUnityPlugin)_basePlugin).Info.Location)); protected string SoundPathForPlugin => Path.Combine("Sounds", PluginFriendlyName); protected string FullSoundPathForPlugin => Path.Combine(Paths.PluginPath, PluginDir, Path.Combine("Sounds", PluginFriendlyName)); public abstract void Start(); protected static void Log(UnifiedChaosPlugin basePlugin, LogLevel logLevel, string pluginName, string message) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) basePlugin.Log(logLevel, pluginName, message); } protected void Log(LogLevel logLevel, string message) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) Log(_basePlugin, logLevel, PluginFriendlyName, message); } protected void LogDebug(string message) { Log((LogLevel)32, message); } protected void LogMessage(string message) { Log((LogLevel)8, message); } protected void LogAll(string message) { Log((LogLevel)63, message); } protected void LogError(string message) { Log((LogLevel)2, message); } protected void LogFatal(string message) { Log((LogLevel)1, message); } protected void LogInfo(string message) { Log((LogLevel)16, message); } protected void LogNone(string message) { Log((LogLevel)0, message); } protected void LogWarning(string message) { Log((LogLevel)4, message); } protected void LoadAudio(IEnumerable<string> audioIds) { string[] validExtensions = new string[3] { ".ogg", ".wav", ".mp3" }; Func<FileInfo, string, bool> selector = (FileInfo f, string s) => f.Name.StartsWith(s, ignoreCase: true, CultureInfo.InvariantCulture); Func<FileInfo, bool> filter = (FileInfo f) => validExtensions.Contains(f.Extension.ToLower()); if (!audioIds.Any()) { return; } string fullSoundPathForPlugin = FullSoundPathForPlugin; if (!Directory.Exists(Path.Combine(fullSoundPathForPlugin))) { throw new DirectoryNotFoundException(fullSoundPathForPlugin); } DirectoryInfo directoryInfo = new DirectoryInfo(fullSoundPathForPlugin); FileInfo[] source = (from f in directoryInfo.EnumerateFiles() where audioIds.Any((string i) => filter(f) && selector(f, i)) select f).ToArray(); foreach (string audioId in audioIds) { if (!_audioClips.ContainsKey(audioId)) { _audioClips.Add(audioId, new List<AudioClip>()); } foreach (FileInfo item in source.Where((FileInfo f) => selector(f, audioId))) { LogDebug("Audio Found:('" + audioId + "')" + item.Name); AudioClip audioClip = SoundTool.GetAudioClip(PluginDir, SoundPathForPlugin, item.Name); _audioClips[audioId].Add(audioClip); } } } [CanBeNull] protected AudioClip GetPreloadedAudioClip(string key) { if (!_audioClips.ContainsKey(key) || !_audioClips[key].Any()) { return null; } return _audioClips[key].First(); } protected AudioClip[] GetPreloadedAudioClips(string key, [CanBeNull] AudioClip[] original = null) { object source; if (!_audioClips.ContainsKey(key) || !_audioClips[key].Any()) { IEnumerable<AudioClip> enumerable = original; source = enumerable ?? Enumerable.Empty<AudioClip>(); } else { IEnumerable<AudioClip> enumerable = _audioClips[key]; source = enumerable; } return ((IEnumerable<AudioClip>)source).ToArray(); } protected AudioClip[] GetPreloadedAudioClips(IEnumerable<string> keys, [CanBeNull] AudioClip[] original = null) { IEnumerable<AudioClip> enumerable = _audioClips.Where((KeyValuePair<string, List<AudioClip>> c) => keys.Contains(c.Key)).SelectMany((KeyValuePair<string, List<AudioClip>> c) => c.Value); object source; if (!enumerable.Any()) { source = original ?? Enumerable.Empty<AudioClip>(); } else { source = enumerable; } return ((IEnumerable<AudioClip>)source).ToArray(); } } } namespace TeamChaos.UnifiedChaosPack.PluginManagers.MobPlus { public class BaboonBirdExtender : MobPlusPatcher<BaboonBirdExtender, BaboonBirdAI> { protected BaboonBirdExtender() { } } public class BlobExtender : MobPlusPatcher<BlobExtender, BlobAI> { protected BlobExtender() { } } public class CentipedeExtender : MobPlusPatcher<CentipedeExtender, CentipedeAI> { protected CentipedeExtender() { } } public class CrawlerExtender : MobPlusPatcher<CrawlerExtender, CrawlerAI> { protected CrawlerExtender() { } } public class DocileLocustBeesExtender : MobPlusPatcher<DocileLocustBeesExtender, DocileLocustBeesAI> { protected DocileLocustBeesExtender() { } } public class DoublewingExtender : MobPlusPatcher<DoublewingExtender, DoublewingAI> { protected DoublewingExtender() { } } public class DressGirlExtender : MobPlusPatcher<DressGirlExtender, DressGirlAI> { protected DressGirlExtender() { } } public class FlowermanExtender : MobPlusPatcher<FlowermanExtender, FlowermanAI> { public enum BehaviorState { Search, Moving, Angry } public event Action<FlowermanAI, PlayerControllerB> AngryAtPlayer; public event Action<FlowermanAI, bool> DroppingBody; public event Action<FlowermanAI, PlayerControllerB> EvadingPlayer; public event Action<FlowermanAI, bool, PlayerControllerB> KilledPlayer; public event Action<FlowermanAI, PlayerControllerB> StaringDownPlayer; protected FlowermanExtender() { base.ChangeBehaviorStateLocalClient += OnChangeBehaviorStateLocalClient; } [HarmonyPrefix] [HarmonyPatch(typeof(FlowermanAI), "DropPlayerBody")] public static void __DropPlayerBody(FlowermanAI __instance) { if (__instance.carryingPlayerBody) { MobPlusPatcher<FlowermanExtender, FlowermanAI>._instance.DroppingBody?.Invoke(__instance, MobPlusPatcher<FlowermanExtender, FlowermanAI>._instance.NearFavoriteSpot(__instance)); } } [HarmonyPrefix] [HarmonyPatch(typeof(FlowermanAI), "FinishKillAnimation")] public static void __FinishKillAnimation(FlowermanAI __instance, bool carryingBody) { MobPlusPatcher<FlowermanExtender, FlowermanAI>._instance.KilledPlayer?.Invoke(__instance, carryingBody, __instance.bodyBeingCarried?.playerScript); } [HarmonyPostfix] [HarmonyPatch(typeof(FlowermanAI), "LookAtFlowermanTrigger")] public static void __LookAtFlowermanTrigger(FlowermanAI __instance, bool __state) { if (__instance.evadeModeStareDown && !__state) { MobPlusPatcher<FlowermanExtender, FlowermanAI>._instance.StaringDownPlayer?.Invoke(__instance, ((EnemyAI)__instance).targetPlayer); } } [HarmonyPrefix] [HarmonyPatch(typeof(FlowermanAI), "LookAtFlowermanTrigger")] public static void __LookAtFlowermanTriggerPrefix(FlowermanAI __instance, ref bool __state) { __state = __instance.evadeModeStareDown; } [HarmonyPostfix] [HarmonyPatch(typeof(FlowermanAI), "Update")] public static void __Update(FlowermanAI __instance, bool __state) { if (__instance.wasInEvadeMode && !__state) { MobPlusPatcher<FlowermanExtender, FlowermanAI>._instance.EvadingPlayer?.Invoke(__instance, ((EnemyAI)__instance).targetPlayer); } } [HarmonyPrefix] [HarmonyPatch(typeof(FlowermanAI), "Update")] public static void __UpdatePrefix(FlowermanAI __instance, ref bool __state) { __state = __instance.wasInEvadeMode; } private bool NearFavoriteSpot(FlowermanAI ai) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) return (double)Vector3.Distance(((Component)ai).transform.position, ((EnemyAI)ai).favoriteSpot.position) < 7.0; } private void OnChangeBehaviorStateLocalClient(FlowermanAI ai, int state) { switch ((BehaviorState)state) { case BehaviorState.Angry: this.AngryAtPlayer?.Invoke(ai, ((EnemyAI)ai).targetPlayer); break; default: UnifiedChaosPlugin.Instance.LogWarning("FlowermanExtender", $"Unknown behavior state: {state}"); break; case BehaviorState.Search: case BehaviorState.Moving: break; } } } public class ForestGiantExtender : MobPlusPatcher<ForestGiantExtender, ForestGiantAI> { protected ForestGiantExtender() { } } public class HoarderBugExtender : MobPlusPatcher<HoarderBugExtender, HoarderBugAI> { public enum BehaviorState { Searching, Resting, Angry } public event Action<HoarderBugAI, PlayerControllerB> AngryAtPlayer; public event Action<HoarderBugAI, PlayerControllerB> ChangedWatchingPlayer; public event Action<HoarderBugAI, GrabbableObject> HuntingItem; public event Action<HoarderBugAI, HoarderBugItem> ItemDroppedInNest; public event Action<HoarderBugAI, HoarderBugItem> ItemGrabbed; protected HoarderBugExtender() { base.ChangeBehaviorStateLocalClient += OnChangeBehaviorStateLocalClient; } [HarmonyPatch(typeof(HoarderBugAI), "DetectAndLookAtPlayers")] [HarmonyTranspiler] public static IEnumerable<CodeInstruction> __ChangedWatchingPlayer(IEnumerable<CodeInstruction> instructions) { MethodInfo methodInfo = AccessTools.Method(typeof(HoarderBugExtender), "__ChangedWatchingPlayerHook", new Type[1] { typeof(HoarderBugAI) }, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(RoundManager), "PlayRandomClip", new Type[5] { typeof(AudioSource), typeof(AudioClip[]), typeof(bool), typeof(float), typeof(int) }, (Type[])null); List<CodeInstruction> list = new List<CodeInstruction>(); foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, methodInfo2)) { list.RemoveRange(list.Count - 7, 7); list.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null)); list.Add(new CodeInstruction(OpCodes.Call, (object)methodInfo)); } else { list.Add(instruction); } } foreach (CodeInstruction item in list) { yield return item; } } private static int __ChangedWatchingPlayerHook(HoarderBugAI ai) { MobPlusPatcher<HoarderBugExtender, HoarderBugAI>._instance.OnChangedWatchingPlayer(ai); return int.MinValue; } [HarmonyPostfix] [HarmonyPatch(typeof(HoarderBugAI), "DropItem")] public static void __DropItem(HoarderBugAI __instance, bool droppingInNest, HoarderBugItem __state) { if (droppingInNest) { MobPlusPatcher<HoarderBugExtender, HoarderBugAI>._instance.ItemDroppedInNest?.Invoke(__instance, __state); } } [HarmonyPrefix] [HarmonyPatch(typeof(HoarderBugAI), "DropItem")] public static void __DropItemPrefix(HoarderBugAI __instance, ref HoarderBugItem __state) { __state = __instance.heldItem; } [HarmonyPostfix] [HarmonyPatch(typeof(HoarderBugAI), "GrabTargetItemIfClose")] public static void __GrabTargetItemIfClose(HoarderBugAI __instance, bool __result) { if (__result) { MobPlusPatcher<HoarderBugExtender, HoarderBugAI>._instance.ItemGrabbed?.Invoke(__instance, __instance.heldItem); } } private void OnChangeBehaviorStateLocalClient(HoarderBugAI ai, int state) { switch ((BehaviorState)state) { case BehaviorState.Searching: this.HuntingItem?.Invoke(ai, ai.targetItem); break; case BehaviorState.Angry: this.AngryAtPlayer?.Invoke(ai, ((EnemyAI)ai).targetPlayer); break; default: UnifiedChaosPlugin.Instance.LogWarning("HoarderBugExtender", $"Unknown behavior state: {state}"); break; case BehaviorState.Resting: break; } } private void OnChangedWatchingPlayer(HoarderBugAI ai) { this.ChangedWatchingPlayer?.Invoke(ai, ai.watchingPlayer); } } public class JesterExtender : MobPlusPatcher<JesterExtender, JesterAI> { protected JesterExtender() { } } public class LassoManExtender : MobPlusPatcher<LassoManExtender, LassoManAI> { protected LassoManExtender() { } } public class MaskedPlayerEnemyExtender : MobPlusPatcher<MaskedPlayerEnemyExtender, MaskedPlayerEnemy> { protected MaskedPlayerEnemyExtender() { } } public static class Extensions { public static Dictionary<Type, EventBroker> InitEventBroker<T>(this Dictionary<Type, EventBroker> collection, T instance, Action<T> initializer) where T : EventBroker, new() { if (collection.ContainsKey(typeof(T))) { return collection; } collection.Add(instance.EnemyType, instance); initializer(instance); return collection; } public static Dictionary<Type, MobPlusPatcher> InitPatcher<T>(this Dictionary<Type, MobPlusPatcher> collection, T instance, Action<T> initializer) where T : MobPlusPatcher { if (collection.ContainsKey(typeof(T))) { return collection; } collection.Add(instance.EnemyType, instance); initializer(instance); return collection; } } public abstract class MobPlusPatcher<TMobPlus, TEnemyAI> : MobPlusPatcher<TEnemyAI> where TMobPlus : MobPlusPatcher<TMobPlus, TEnemyAI> where TEnemyAI : EnemyAI { protected class CommonModPlusPatches { public static Harmony HarmonyInstance { get; } = new Harmony(Guid.NewGuid().ToString()); public static void __OnDestroy(TEnemyAI __instance) { UnifiedChaosPlugin.Instance.LogDebug("CommonModPlusPatches+" + typeof(TEnemyAI).Name + "::__OnDestroy", $"Instance:'{MobPlusPatcher<TMobPlus, TEnemyAI>._instance.EnemyType.Name}', IsNull:'{(Object)(object)__instance == (Object)null}'", verboseOnly: true); MobPlusPatcher<TMobPlus, TEnemyAI>._instance.EndTrackObject(__instance); } public static void __Start(TEnemyAI __instance) { } public static void __SwitchToBehaviourStateOnLocalClient(TEnemyAI __instance, int stateIndex) { if (!((Object)(object)MobPlusPatcher<TMobPlus, TEnemyAI>._instance.GetNetworkObject(((NetworkBehaviour)(object)__instance).NetworkObjectId) == (Object)null)) { UnifiedChaosPlugin.Instance.LogDebug("CommonModPlusPatches+" + typeof(TEnemyAI).Name + "::__SwitchToBehaviourStateOnLocalClient", $"Instance:'{MobPlusPatcher<TMobPlus, TEnemyAI>._instance.EnemyType.Name}', IsNull:'{(Object)(object)__instance == (Object)null}'", verboseOnly: true); ((MobPlusPatcher<TMobPlus, TEnemyAI>)MobPlusPatcher<TMobPlus, TEnemyAI>._instance).ChangeBehaviorStateLocalClient?.Invoke(__instance, stateIndex); } } private static MethodInfo FindMethod(string type, string methodName) { MethodInfo method = AccessTools.TypeByName(type).GetMethod(methodName); if (method?.DeclaringType != null && method.DeclaringType?.Name != type) { method = AccessTools.TypeByName(method.DeclaringType.Name).GetMethod(methodName); } return method; } public static void PatchMethods() { PostfixMethod(typeof(TEnemyAI).Name, "Start", "__Start"); PostfixMethod(typeof(TEnemyAI).Name, "OnDestroy", "__OnDestroy"); PrefixMethod(typeof(TEnemyAI).Name, "SwitchToBehaviourStateOnLocalClient", "__SwitchToBehaviourStateOnLocalClient"); } public static void PostfixMethod(string targetType, string methodName, string newMethodName) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown MethodInfo methodInfo = FindMethod(targetType, methodName); MethodInfo method = typeof(CommonModPlusPatches).GetMethod(newMethodName); HarmonyInstance.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } public static void PrefixMethod(string targetType, string methodName, string newMethodName) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown MethodInfo methodInfo = FindMethod(targetType, methodName); MethodInfo method = typeof(CommonModPlusPatches).GetMethod(newMethodName); HarmonyInstance.Patch((MethodBase)methodInfo, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } protected static TMobPlus _instance; private static readonly object _createLock = new object(); public event Action<TEnemyAI, int> ChangeBehaviorStateLocalClient; public virtual void ApplyPatches() { Harmony.CreateAndPatchAll(typeof(TMobPlus), (string)null); CommonModPlusPatches.PatchMethods(); } public static TMobPlus GetInstance() { if (_instance == null) { lock (_createLock) { ConstructorInfo constructor = typeof(TMobPlus).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, Array.Empty<Type>(), Array.Empty<ParameterModifier>()); if (constructor == null) { throw new Exception("Type '" + typeof(TMobPlus).Name + "' missing protected constructor!"); } _instance = constructor.Invoke(Array.Empty<object>()) as TMobPlus; _instance.ApplyPatches(); } } return _instance; } } public abstract class MobPlusPatcher<TEnemyAI> : MobPlusPatcher where TEnemyAI : EnemyAI { public override Type EnemyType => typeof(TEnemyAI); protected void BeginTrackObject(TEnemyAI trackedObject) { if (!((Object)(object)trackedObject == (Object)null)) { ulong? networkId = GetNetworkId(trackedObject); if (networkId.HasValue && !_trackedObjects.ContainsKey(networkId.Value)) { _trackedObjects.Add(networkId.Value, (EnemyAI)(object)trackedObject); } } } protected void EndTrackObject(TEnemyAI trackedObject) { if (!((Object)(object)trackedObject == (Object)null)) { ulong? networkId = GetNetworkId(trackedObject); if (networkId.HasValue && _trackedObjects.ContainsKey(networkId.Value)) { _trackedObjects.Remove(networkId.Value); } } } protected ulong? GetNetworkId(TEnemyAI ai) { object obj = ai; if (obj == null) { return null; } NetworkObject networkObject = ((NetworkBehaviour)obj).NetworkObject; if (networkObject == null) { return null; } return networkObject.NetworkObjectId; } public new TEnemyAI GetNetworkObject(ulong id) { if (!_trackedObjects.TryGetValue(id, out var value)) { return default(TEnemyAI); } return (TEnemyAI)(object)((value is TEnemyAI) ? value : null); } } public abstract class MobPlusPatcher { protected Dictionary<ulong, EnemyAI> _trackedObjects = new Dictionary<ulong, EnemyAI>(); public abstract Type EnemyType { get; } protected void BeginTrackObject(EnemyAI trackedObject) { if (!((Object)(object)trackedObject == (Object)null)) { ulong networkId = GetNetworkId(trackedObject); if (!_trackedObjects.ContainsKey(networkId)) { _trackedObjects.Add(networkId, trackedObject); } } } protected void EndTrackObject(EnemyAI trackedObject) { if (!((Object)(object)trackedObject == (Object)null)) { ulong networkId = GetNetworkId(trackedObject); if (_trackedObjects.ContainsKey(networkId)) { _trackedObjects.Remove(networkId); } } } public static ulong GetNetworkId(EnemyAI ai) { return ((NetworkBehaviour)ai).NetworkObject.NetworkObjectId; } public EnemyAI GetNetworkObject(ulong id) { if (!_trackedObjects.TryGetValue(id, out var value)) { return null; } return value; } public void TestDumpTracked() { UnifiedChaosPlugin.Instance.LogDebug(GetType().Name, "Dumping known objects:"); foreach (KeyValuePair<ulong, EnemyAI> trackedObject in _trackedObjects) { UnifiedChaosPlugin instance = UnifiedChaosPlugin.Instance; string name = GetType().Name; object arg = trackedObject.Key; EnemyAI value = trackedObject.Value; instance.LogDebug(name, $"\tKey:'{arg}', Value:'{((value != null) ? ((Object)value).name : null)}'"); } } } public class MouthDogExtender : MobPlusPatcher<MouthDogExtender, MouthDogAI> { protected MouthDogExtender() { } } public class NutcrackerEnemyExtender : MobPlusPatcher<NutcrackerEnemyExtender, NutcrackerEnemyAI> { protected NutcrackerEnemyExtender() { } } public class PufferExtender : MobPlusPatcher<PufferExtender, PufferAI> { protected PufferExtender() { } } public class RedLocustBeesExtender : MobPlusPatcher<RedLocustBeesExtender, RedLocustBees> { protected RedLocustBeesExtender() { } } public class SandSpiderExtender : MobPlusPatcher<SandSpiderExtender, SandSpiderAI> { protected SandSpiderExtender() { } } public class SandWormExtender : MobPlusPatcher<SandWormExtender, SandWormAI> { protected SandWormExtender() { } } } namespace TeamChaos.UnifiedChaosPack.PluginManagers.MobPlus.Notifiers { public class FlowermanAINotifier : Notifier<FlowermanAI, FlowermanEventBroker> { public enum BehaviorState { Sneaking, Evading, Threaten } private class FlowermanNotifierCoroutineManager : CoroutineManager<FlowermanAINotifier> { private const float DEFAULT_DELAY = 1.5f; private const float DEFAULT_DEVIATION = 0.5f; private readonly RandomDelayCoroutine _chase; private readonly RandomDelayCoroutine _dropItem; protected internal event Action ChaseEventTrigger; protected internal event Action DropItemEventTrigger; public FlowermanNotifierCoroutineManager(FlowermanAINotifier notifier) : base(notifier) { _chase = new RandomDelayCoroutine((MonoBehaviour)(object)notifier, 1f, 2f); _dropItem = new RandomDelayCoroutine((MonoBehaviour)(object)notifier, 1f, 2f); } public void ChaseStart(bool restart = false) { _chase.Start(restart, delegate { this.ChaseEventTrigger?.Invoke(); }); } public void ChaseStop() { _chase.Stop(); } public void DropItemStart(Action action, bool restart = false) { _dropItem.Start(restart, action); } public void DropItemStop() { _dropItem.Stop(); } } private readonly FlowermanNotifierCoroutineManager _coroutineManager; private BehaviorState _behaviorState; public PlayerControllerB PlayerKilled => _ai?.bodyBeingCarried?.playerScript; public PlayerControllerB TargetPlayer => ((EnemyAI)(_ai?)).targetPlayer; public bool AtFavoriteSpot => (double)Vector3.Distance(((Component)_ai).transform.position, ((EnemyAI)_ai).favoriteSpot.position) < 7.0; public bool CarryingBody { get; private set; } public FlowermanAINotifier() { _coroutineManager = new FlowermanNotifierCoroutineManager(this); _coroutineManager.ChaseEventTrigger += CoroutineManagerOnChaseEventTrigger; } public void KilledPlayer(bool carryingBody) { CarryingBody = carryingBody; _broker.AIEventNotify(this, "KilledPlayer"); } public void DroppingBody() { _broker.AIEventNotify(this, "KilledPlayer"); } public void ChangedWatchingPlayer() { _broker.AIEventNotify(this, "ChangedWatchingPlayer"); } private void CoroutineManagerOnChaseEventTrigger() { _broker.AIEventNotify(this, "AngryAtPlayer"); } public override void Update() { } protected override void Initialize() { } protected override void OnChangeBehaviorStateLocalClient(int stateIndex) { BehaviorState enumValue = GetEnumValue<BehaviorState>(stateIndex); BehaviorState previousBehaviorState = GetPreviousBehaviorState<BehaviorState>(); switch (enumValue) { case BehaviorState.Threaten: _broker.AIEventNotify(this, "AngryAtPlayer"); break; default: UnifiedChaosPlugin.Instance.LogWarning(((object)this).GetType().Name + "::OnChangeBehaviorStateLocalClient", $"Unknown behavior state: {stateIndex}"); break; case BehaviorState.Sneaking: case BehaviorState.Evading: break; } } public void StaringDownPlayer() { _broker.AIEventNotify(this, "StaringDownPlayer"); } public void EvadingPlayer() { _broker.AIEventNotify(this, "EvadingPlayer"); } } public class HoarderBugAINotifier : Notifier<HoarderBugAI, HoarderBugEventBroker> { public enum BehaviorState { Scavenging, Returning, Chasing } private class HoarderBugNotifierCoroutineManager : CoroutineManager<HoarderBugAINotifier> { private const float DEFAULT_DELAY = 1.5f; private const float DEFAULT_DEVIATION = 0.5f; private readonly RandomDelayCoroutine _chase; private readonly RandomDelayCoroutine _dropItem; protected internal event Action ChaseEventTrigger; protected internal event Action DropItemEventTrigger; public HoarderBugNotifierCoroutineManager(HoarderBugAINotifier notifier) : base(notifier) { _chase = new RandomDelayCoroutine((MonoBehaviour)(object)notifier, 1f, 2f); _dropItem = new RandomDelayCoroutine((MonoBehaviour)(object)notifier, 1f, 2f); } public void ChaseStart(bool restart = false) { _chase.Start(restart, delegate { this.ChaseEventTrigger?.Invoke(); }); } public void ChaseStop() { _chase.Stop(); } public void DropItemStart(Action action, bool restart = false) { _dropItem.Start(restart, action); } public void DropItemStop() { _dropItem.Stop(); } } private readonly HoarderBugNotifierCoroutineManager _coroutineManager; private PlayerControllerB _angryAtPlayer; private BehaviorState _behaviorState; private HoarderBugItem _heldItem; private Notifier<HoarderBugAI, HoarderBugEventBroker> _notifierImplementation; public HoarderBugAINotifier() { _coroutineManager = new HoarderBugNotifierCoroutineManager(this); _coroutineManager.ChaseEventTrigger += CoroutineManagerOnChaseEventTrigger; } public void ChangedWatchingPlayer() { _broker.AIEventNotify(this, "ChangedWatchingPlayer"); } private void CheckForAngryPlayerChanged() { BehaviorState behaviorState = GetBehaviorState<BehaviorState>(); if (_behaviorState != behaviorState) { _behaviorState = behaviorState; } if (behaviorState == BehaviorState.Chasing && (Object)(object)_angryAtPlayer != (Object)(object)_ai.angryAtPlayer) { UnifiedChaosPlugin.Instance.LogWarning(((object)this).GetType().Name + "::CheckForAngryPlayerChanged", "Trigger Change Target"); _angryAtPlayer = _ai.angryAtPlayer; _coroutineManager.ChaseStart(restart: true); } } private void CheckForChangedWatchingPlayer() { } private void CheckForHuntingItem() { } private void CheckForItemDroppedInNest() { } private void CheckForItemGrabbed() { if (_heldItem != _ai.heldItem) { _heldItem = _ai.heldItem; _broker.AIEventNotify(this, "ItemGrabbed"); } } private void CoroutineManagerOnChaseEventTrigger() { _broker.AIEventNotify(this, "AngryAtPlayer"); } public void DropItem(bool droppingInNest) { _coroutineManager.DropItemStart(delegate { _heldItem = _ai.heldItem; _broker.AIEventNotify(this, droppingInNest ? "ItemDroppedInNest" : "ItemDropped"); }); } protected override void Initialize() { } protected override void OnChangeBehaviorStateLocalClient(int stateIndex) { BehaviorState enumValue = GetEnumValue<BehaviorState>(stateIndex); BehaviorState previousBehaviorState = GetPreviousBehaviorState<BehaviorState>(); switch (enumValue) { case BehaviorState.Scavenging: _coroutineManager.ChaseStop(); _coroutineManager.DropItemStop(); break; case BehaviorState.Returning: _coroutineManager.ChaseStop(); _coroutineManager.DropItemStop(); break; case BehaviorState.Chasing: UnifiedChaosPlugin.Instance.LogInfo(((object)this).GetType().Name + "::OnChangeBehaviorStateLocalClient", "Chase Start"); _coroutineManager.DropItemStop(); _angryAtPlayer = _ai.angryAtPlayer; _coroutineManager.ChaseStart(); break; default: UnifiedChaosPlugin.Instance.LogWarning(((object)this).GetType().Name + "::OnChangeBehaviorStateLocalClient", $"Unknown behavior state: {stateIndex}"); break; } } public override void Update() { CheckForAngryPlayerChanged(); CheckForChangedWatchingPlayer(); CheckForHuntingItem(); CheckForItemDroppedInNest(); CheckForItemGrabbed(); } } public abstract class Notifier : MonoBehaviour { public abstract ulong NetworkObjectId { get; } public abstract void Update(); protected abstract void Initialize(); } public abstract class Notifier<TEnemyAI> : Notifier where TEnemyAI : EnemyAI { protected TEnemyAI _ai; public TEnemyAI AiInstance => _ai; public override ulong NetworkObjectId => ((NetworkBehaviour)(object)_ai).NetworkObjectId; protected T GetBehaviorState<T>() where T : struct, Enum { return GetEnumValue<T>(((EnemyAI)_ai).currentBehaviourStateIndex); } protected T GetEnumValue<T>(object value) where T : struct, Enum { if (!Enum.TryParse<T>((value as string) ?? Convert.ToString(value), out var result)) { return default(T); } return result; } protected T GetPreviousBehaviorState<T>() where T : struct, Enum { return GetEnumValue<T>(((EnemyAI)_ai).previousBehaviourStateIndex); } } public abstract class Notifier<TEnemyAI, TEventBroker> : Notifier<TEnemyAI> where TEnemyAI : EnemyAI where TEventBroker : EventBroker { protected class NotifierPatches { [HarmonyPostfix] [HarmonyPatch(typeof(EnemyAI), "SwitchToBehaviourStateOnLocalClient")] public static void __EnemyAI_SwitchToBehaviourStateOnLocalClient(EnemyAI __instance, int stateIndex) { if (__instance is TEnemyAI) { UnifiedChaosPlugin.Instance.LogDebug(string.Format("{0}::{1}", typeof(TEnemyAI), "__EnemyAI_SwitchToBehaviourStateOnLocalClient"), "Method called!", verboseOnly: true); Notifier<TEnemyAI, TEventBroker> component = ((Component)__instance).gameObject.GetComponent<Notifier<TEnemyAI, TEventBroker>>(); if (Object.op_Implicit((Object)(object)component)) { component.OnChangeBehaviorStateLocalClient(stateIndex); } } } } private static Guid? _patchId; private static bool _isPatched; private readonly object _patchLock = new object(); protected TEventBroker _broker; static Notifier() { } protected virtual void OnDestroy() { UnifiedChaosPlugin.Instance.LogDebug(((object)this).GetType().Name + "::OnDestroy", "Method called!"); _broker.UnregisterNotifier(this); } protected virtual void ApplyPatches() { Harmony.CreateAndPatchAll(typeof(NotifierPatches), (string)null); } public void Initialize(TEnemyAI aiInstance, TEventBroker brokerInstance) { _ai = aiInstance; _broker = brokerInstance; Initialize(); _broker.RegisterNotifier(this); lock (_patchLock) { if (_isPatched) { return; } _isPatched = true; } ApplyPatches(); } protected abstract void OnChangeBehaviorStateLocalClient(int stateIndex); } public class SpringManAINotifier : Notifier<SpringManAI, SpringManEventBroker> { public enum BehaviorState { Roaming, Chasing } private class SpringManNotifierCoroutineManager : CoroutineManager<SpringManAINotifier> { public enum StareEventArgs { Short, Medium, Long } private readonly RandomDelayCoroutine _stareLong; private readonly RandomDelayCoroutine _stareMedium; private readonly RandomDelayCoroutine _stareShort; protected internal event Action<StareEventArgs> StareEventTrigger; protected internal SpringManNotifierCoroutineManager(SpringManAINotifier notifier) : base(notifier) { _stareShort = new RandomDelayCoroutine((MonoBehaviour)(object)notifier, 1f, 3f); _stareMedium = new RandomDelayCoroutine((MonoBehaviour)(object)notifier, 5f, 15f); _stareLong = new RandomDelayCoroutine((MonoBehaviour)(object)notifier, 30f, 60f); } private void StareLStart() { _stareMedium.Start(delegate { this.StareEventTrigger?.Invoke(StareEventArgs.Long); }); } private void StareMStart() { _stareMedium.Start(delegate { this.StareEventTrigger?.Invoke(StareEventArgs.Medium); StareLStart(); }); } private void StareSStart() { _stareShort.Start(delegate { this.StareEventTrigger?.Invoke(StareEventArgs.Short); StareMStart(); }); } public void StareStart() { StareStart(restart: false); } public void StareStart(bool restart) { if (restart) { StareStop(); } _stareShort.Start(StareSStart); } public void StareStop() { _stareShort.Stop(); _stareMedium.Stop(); _stareLong.Stop(); } } private readonly SpringManNotifierCoroutineManager _coroutineManager; private bool _lastStoppingMovement; public SpringManAINotifier() { _coroutineManager = new SpringManNotifierCoroutineManager(this); _coroutineManager.StareEventTrigger += CoroutineManagerOnStareEventTrigger; } private void CoroutineManagerOnStareEventTrigger(SpringManNotifierCoroutineManager.StareEventArgs e) { switch (e) { case SpringManNotifierCoroutineManager.StareEventArgs.Short: _broker.AIEventNotify(this, "StaringAtPlayer"); break; case SpringManNotifierCoroutineManager.StareEventArgs.Medium: _broker.AIEventNotify(this, "StaringAtPlayerForAWhile"); break; case SpringManNotifierCoroutineManager.StareEventArgs.Long: _broker.AIEventNotify(this, "StaringAtPlayerForALongTime"); break; default: throw new ArgumentOutOfRangeException("e", e, null); } } public override void Update() { CheckForStaringAtPlayer(); } private void CheckForStaringAtPlayer() { if (_lastStoppingMovement != _ai.stoppingMovement) { _lastStoppingMovement = _ai.stoppingMovement; Action action = (_lastStoppingMovement ? new Action(_coroutineManager.StareStart) : new Action(_coroutineManager.StareStop)); action(); } } protected override void Initialize() { } protected override void OnChangeBehaviorStateLocalClient(int stateIndex) { BehaviorState enumValue = GetEnumValue<BehaviorState>(stateIndex); if ((uint)enumValue > 1u) { UnifiedChaosPlugin.Instance.LogWarning(((object)this).GetType().Name + "::OnChangeBehaviorStateLocalClient", $"Unknown behavior state: {stateIndex}"); } } } } namespace TeamChaos.UnifiedChaosPack.PluginManagers.MobPlus.EventBrokers { public abstract class EventBroker { public abstract Type EnemyType { get; } public abstract EnemyAI GetEnemyInstanceByNetworkId(ulong id); public abstract void RegisterNotifier(Notifier notifier); public abstract void UnregisterNotifier(Notifier notifier); } public abstract class EventBroker<TNotifier> : EventBroker where TNotifier : Notifier { protected readonly Dictionary<ulong, TNotifier> _notifiers = new Dictionary<ulong, TNotifier>(); protected internal abstract void ApplyPatches(); public override void RegisterNotifier(Notifier notifier) { if (notifier is TNotifier) { if (_notifiers.ContainsKey(notifier.NetworkObjectId)) { _notifiers.Remove(notifier.NetworkObjectId); } _notifiers.Add(notifier.NetworkObjectId, (TNotifier)notifier); } } public override void UnregisterNotifier(Notifier notifier) { if (notifier is TNotifier && _notifiers.ContainsKey(notifier.NetworkObjectId)) { _notifiers.Remove(notifier.NetworkObjectId); } } } public abstract class EventBroker<TNotifier, TEnemyAI, TEventBroker> : EventBroker<TNotifier> where TNotifier : Notifier<TEnemyAI, TEventBroker> where TEnemyAI : EnemyAI where TEventBroker : EventBroker<TNotifier>, new() { protected class EventBrokerPatches { [HarmonyPostfix] [HarmonyPatch(typeof(EnemyAI), "Start")] public static void __EnemyAI_Start(EnemyAI __instance) { if (__instance is TEnemyAI) { UnifiedChaosPlugin.Instance.LogDebug(string.Format("{0}::{1}", typeof(TEnemyAI), "__EnemyAI_Start"), "Method called!"); ((Component)__instance).gameObject.AddComponent<TNotifier>().Initialize((TEnemyAI)(object)__instance, EventBroker<TNotifier, TEnemyAI, TEventBroker>._instance); DumpBehaviourStates(__instance); } } public static void Apply() { throw new NotImplementedException(); } private static void DumpBehaviourStates(EnemyAI instance) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(); stringBuilder.AppendLine(" /// <summary>"); stringBuilder.AppendLine(" /// EnemyBehaviorStates:"); for (int j = 0; j < instance.enemyBehaviourStates.Length; j++) { EnemyBehaviourState val = instance.enemyBehaviourStates[j]; stringBuilder.AppendLine(" /// --------"); stringBuilder.AppendLine($" /// index:{j}"); stringBuilder.AppendLine(" /// Name:" + val.name); stringBuilder.AppendLine($" /// playOneShotSFX:{val.playOneShotSFX}"); AudioClip sFXClip = val.SFXClip; stringBuilder.AppendLine(" /// SFXClip:" + (((sFXClip != null) ? ((Object)sFXClip).name : null) ?? "null")); stringBuilder.AppendLine($" /// playOneShotVoice:{val.playOneShotVoice}"); AudioClip voiceClip = val.VoiceClip; stringBuilder.AppendLine(" /// VoiceClip:" + (((voiceClip != null) ? ((Object)voiceClip).name : null) ?? "null")); stringBuilder.AppendLine($" /// boolValue:{val.boolValue}"); stringBuilder.AppendLine($" /// IsAnimTrigger:{val.IsAnimTrigger}"); stringBuilder.AppendLine(" /// parameterString:" + val.parameterString); } stringBuilder.AppendLine(" /// </summary>"); stringBuilder.Append(" private enum BehaviorState { ").AppendJoin(", ", instance.enemyBehaviourStates.Select((EnemyBehaviourState s, int i) => $"{StringUtilsExtensions.ToPascalCase(s.name)} = {i}")).AppendLine(" }"); UnifiedChaosPlugin.Instance.LogDebug("EventBrokerPatches+" + typeof(TEnemyAI).Name + "::enemyBehaviourStates", stringBuilder.ToString()); } } private static TEventBroker _instance; private static readonly object _createLock = new object(); public override Type EnemyType => typeof(TEnemyAI); protected static TNotifier GetNotifierInstance(TEnemyAI enemyAI) { return ((Component)(object)enemyAI).gameObject.GetComponent<TNotifier>(); } internal abstract void AIEventNotify(TNotifier notifier, string evtName); protected internal override void ApplyPatches() { Harmony.CreateAndPatchAll(typeof(TEventBroker), (string)null); Harmony.CreateAndPatchAll(typeof(EventBrokerPatches), (string)null); } public override EnemyAI GetEnemyInstanceByNetworkId(ulong id) { object obj; if (!_notifiers.TryGetValue(id, out var value)) { obj = default(TEnemyAI); } else { TNotifier val = value; obj = ((val != null) ? val.AiInstance : default(TEnemyAI)); } return (EnemyAI)(object)(TEnemyAI)obj; } public static TEventBroker GetInstance() { if (_instance == null) { lock (_createLock) { UnifiedChaosPlugin.Instance.LogDebug("SpringManAINotifier::GetInstance", "Creating Instance."); _instance = new TEventBroker(); _instance.ApplyPatches(); } } return _instance; } } public class FlowermanEventBroker : EventBroker<FlowermanAINotifier, FlowermanAI, FlowermanEventBroker> { public event Action<FlowermanAI, PlayerControllerB> AngryAtPlayer; public event Action<FlowermanAI, bool, PlayerControllerB> KilledPlayer; public event Action<FlowermanAI, PlayerControllerB> EvadingPlayer; public event Action<FlowermanAI, PlayerControllerB> StaringDownPlayer; public event Action<FlowermanAI, bool> DroppingBody; [HarmonyPrefix] [HarmonyPatch(typeof(FlowermanAI), "DropPlayerBody")] public static void __DropPlayerBody(FlowermanAI __instance) { if (__instance.carryingPlayerBody) { FlowermanAINotifier notifierInstance = EventBroker<FlowermanAINotifier, FlowermanAI, FlowermanEventBroker>.GetNotifierInstance(__instance); notifierInstance.DroppingBody(); } } [HarmonyPrefix] [HarmonyPatch(typeof(FlowermanAI), "FinishKillAnimation")] public static void __FinishKillAnimation(FlowermanAI __instance, bool carryingBody) { FlowermanAINotifier notifierInstance = EventBroker<FlowermanAINotifier, FlowermanAI, FlowermanEventBroker>.GetNotifierInstance(__instance); notifierInstance.KilledPlayer(carryingBody); } [HarmonyPostfix] [HarmonyPatch(typeof(FlowermanAI), "LookAtFlowermanTrigger")] public static void __LookAtFlowermanTrigger(FlowermanAI __instance, bool __state) { if (!(!__instance.evadeModeStareDown || __state)) { FlowermanAINotifier notifierInstance = EventBroker<FlowermanAINotifier, FlowermanAI, FlowermanEventBroker>.GetNotifierInstance(__instance); notifierInstance.StaringDownPlayer(); } } [HarmonyPrefix] [HarmonyPatch(typeof(FlowermanAI), "LookAtFlowermanTrigger")] public static void __LookAtFlowermanTriggerPrefix(FlowermanAI __instance, ref bool __state) { __state = __instance.evadeModeStareDown; } [HarmonyPostfix] [HarmonyPatch(typeof(FlowermanAI), "Update")] public static void __Update(FlowermanAI __instance, bool __state) { if (!(!__instance.wasInEvadeMode || __state)) { FlowermanAINotifier notifierInstance = EventBroker<FlowermanAINotifier, FlowermanAI, FlowermanEventBroker>.GetNotifierInstance(__instance); notifierInstance.EvadingPlayer(); } } [HarmonyPrefix] [HarmonyPatch(typeof(FlowermanAI), "Update")] public static void __UpdatePrefix(FlowermanAI __instance, ref bool __state) { __state = __instance.wasInEvadeMode; } internal override void AIEventNotify(FlowermanAINotifier notifier, string evtName) { UnifiedChaosPlugin.Instance.LogError(GetType().Name + "::AIEventNotify", string.Format("Event '{0} fired for {1}[{2}]'!", evtName, "HoarderBugAI", ((NetworkBehaviour)notifier.AiInstance).NetworkObjectId)); switch (evtName) { case "KilledPlayer": this.KilledPlayer?.Invoke(notifier.AiInstance, notifier.CarryingBody, notifier.PlayerKilled); break; case "DroppingBody": this.DroppingBody?.Invoke(notifier.AiInstance, notifier.AtFavoriteSpot); break; case "StaringDownPlayer": this.StaringDownPlayer?.Invoke(notifier.AiInstance, notifier.TargetPlayer); break; case "EvadingPlayer": this.StaringDownPlayer?.Invoke(notifier.AiInstance, notifier.TargetPlayer); break; case "AngryAtPlayer": this.AngryAtPlayer?.Invoke(notifier.AiInstance, notifier.TargetPlayer); break; default: UnifiedChaosPlugin.Instance.LogWarning(GetType().Name + "::AIEventNotify", string.Format("Unhandled event '{0} fired for {1}[{2}]'!", evtName, "HoarderBugAI", ((NetworkBehaviour)notifier.AiInstance).NetworkObjectId)); break; } } } public class HoarderBugEventBroker : EventBroker<HoarderBugAINotifier, HoarderBugAI, HoarderBugEventBroker> { public event Action<HoarderBugAI, PlayerControllerB> AngryAtPlayer; public event Action<HoarderBugAI, PlayerControllerB> ChangedWatchingPlayer; public event Action<HoarderBugAI, GrabbableObject> HuntingItem; public event Action<HoarderBugAI> GaveUpChase; public event Action<HoarderBugAI> GaveUpSearch; public event Action<HoarderBugAI, HoarderBugItem> ItemDropped; public event Action<HoarderBugAI, HoarderBugItem> ItemDroppedInNest; public event Action<HoarderBugAI, HoarderBugItem> ItemGrabbed; [HarmonyPatch(typeof(HoarderBugAI), "DetectAndLookAtPlayers")] [HarmonyTranspiler] public static IEnumerable<CodeInstruction> __ChangedWatchingPlayer(IEnumerable<CodeInstruction> instructions) { MethodInfo methodInfo = AccessTools.Method(typeof(HoarderBugEventBroker), "__ChangedWatchingPlayerHook", new Type[1] { typeof(HoarderBugAI) }, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(RoundManager), "PlayRandomClip", new Type[6] { typeof(AudioSource), typeof(AudioClip[]), typeof(bool), typeof(float), typeof(int), typeof(int) }, (Type[])null); List<CodeInstruction> list = new List<CodeInstruction>(); foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, methodInfo2)) { list.RemoveRange(list.Count - 8, 8); list.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null)); list.Add(new CodeInstruction(OpCodes.Call, (object)methodInfo)); } else { list.Add(instruction); } } foreach (CodeInstruction item in list) { yield return item; } } private static int __ChangedWatchingPlayerHook(HoarderBugAI __instance) { UnifiedChaosPlugin.Instance.LogDebug("HoarderBugEventBroker::__ChangedWatchingPlayerHook", "Method called!"); HoarderBugAINotifier component = ((Component)__instance).gameObject.GetComponent<HoarderBugAINotifier>(); component.ChangedWatchingPlayer(); return int.MinValue; } [HarmonyPostfix] [HarmonyPatch(typeof(HoarderBugAI), "DropItemClientRpc")] public static void __DropItemClientRpc(HoarderBugAI __instance, bool droppedInNest) { UnifiedChaosPlugin.Instance.LogDebug("HoarderBugEventBroker::__DropItemClientRpc", "Method called!"); HoarderBugAINotifier component = ((Component)__instance).gameObject.GetComponent<HoarderBugAINotifier>(); component.DropItem(droppedInNest); } internal override void AIEventNotify(HoarderBugAINotifier notifier, string evtName) { UnifiedChaosPlugin.Instance.LogError(GetType().Name + "::AIEventNotify", string.Format("Event '{0} fired for {1}[{2}]'!", evtName, "HoarderBugAI", ((NetworkBehaviour)notifier.AiInstance).NetworkObjectId)); switch (evtName) { case "AngryAtPlayer": this.AngryAtPlayer?.Invoke(notifier.AiInstance, notifier.AiInstance.angryAtPlayer); break; case "ChangedWatchingPlayer": this.ChangedWatchingPlayer?.Invoke(notifier.AiInstance, notifier.AiInstance.watchingPlayer); break; case "GaveUpChase": this.GaveUpChase?.Invoke(notifier.AiInstance); break; case "GaveUpSearch": this.GaveUpSearch?.Invoke(notifier.AiInstance); break; case "ItemDropped": this.ItemDropped?.Invoke(notifier.AiInstance, notifier.AiInstance.heldItem); break; case "ItemDroppedInNest": this.ItemDroppedInNest?.Invoke(notifier.AiInstance, notifier.AiInstance.heldItem); break; default: UnifiedChaosPlugin.Instance.LogWarning(GetType().Name + "::AIEventNotify", string.Format("Unhandled event '{0} fired for {1}[{2}]'!", evtName, "HoarderBugAI", ((NetworkBehaviour)notifier.AiInstance).NetworkObjectId)); break; case "HuntingItem": break; case "ItemGrabbed": break; } } } public class SpringManEventBroker : EventBroker<SpringManAINotifier, SpringManAI, SpringManEventBroker> { public event Action<SpringManAI, PlayerControllerB> StaringAtPlayer; public event Action<SpringManAI, PlayerControllerB> StaringAtPlayerForALongTime; public event Action<SpringManAI, PlayerControllerB> StaringAtPlayerForAWhile; internal override void AIEventNotify(SpringManAINotifier notifier, string evtName) { UnifiedChaosPlugin.Instance.LogDebug(GetType().Name + "::AIEventNotify", string.Format("Event '{0} fired for {1}[{2}]'!", evtName, "SpringManAI", ((NetworkBehaviour)notifier.AiInstance).NetworkObjectId)); switch (evtName) { case "StaringAtPlayer": this.StaringAtPlayer?.Invoke(notifier.AiInstance, IsLookingAtSpringMan(notifier.AiInstance)); break; case "StaringAtPlayerForAWhile": this.StaringAtPlayerForAWhile?.Invoke(notifier.AiInstance, IsLookingAtSpringMan(notifier.AiInstance)); break; case "StaringAtPlayerForALongTime": this.StaringAtPlayerForALongTime?.Invoke(notifier.AiInstance, IsLookingAtSpringMan(notifier.AiInstance)); break; default: UnifiedChaosPlugin.Instance.LogWarning(GetType().Name + "::AIEventNotify", string.Format("Unhandled event '{0} fired for {1}[{2}]'!", evtName, "SpringManAI", ((NetworkBehaviour)notifier.AiInstance).NetworkObjectId)); break; } } [CanBeNull] private PlayerControllerB IsLookingAtSpringMan(SpringManAI ai) { //IL_0019: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) Vector3 lineOfSightPosition = ((Component)ai).transform.position + Vector3.up * 1.6f; float lineOfSightRange = 68