using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Unity.Netcode;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("EnemySoundFixes")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Fixes numerous issues with missing sound effects, or SFX playing when they shouldn't.")]
[assembly: AssemblyFileVersion("1.5.10.0")]
[assembly: AssemblyInformationalVersion("1.5.10+add35d719e56c63ebd85f7c3f6c5795c0209476b")]
[assembly: AssemblyProduct("EnemySoundFixes")]
[assembly: AssemblyTitle("EnemySoundFixes")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.5.10.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace EnemySoundFixes
{
internal enum CruiserMute
{
Nothing = -1,
NotRadio,
All
}
[BepInPlugin("butterystancakes.lethalcompany.enemysoundfixes", "Enemy Sound Fixes", "1.5.10")]
public class Plugin : BaseUnityPlugin
{
private const string PLUGIN_GUID = "butterystancakes.lethalcompany.enemysoundfixes";
private const string PLUGIN_NAME = "Enemy Sound Fixes";
private const string PLUGIN_VERSION = "1.5.10";
internal static ManualLogSource Logger;
internal static ConfigEntry<bool> configFixMasks;
internal static ConfigEntry<bool> configThumperNoThunder;
internal static ConfigEntry<bool> configBetterMimicSteps;
internal static ConfigEntry<CruiserMute> configSpaceMutesCruiser;
private void Awake()
{
//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
Logger = ((BaseUnityPlugin)this).Logger;
configFixMasks = ((BaseUnityPlugin)this).Config.Bind<bool>("Misc", "FixMasks", true, "(Host only, requires game restart) Fixes masks' broken audio intervals.\nDisabling this is useful if you use a voice mimicking mod. (Skinwalkers, Mirage, etc.)");
configBetterMimicSteps = ((BaseUnityPlugin)this).Config.Bind<bool>("Misc", "BetterMimicSteps", false, "Mimic footstep volume and distance are altered to sound more accurate to actual players.");
configThumperNoThunder = ((BaseUnityPlugin)this).Config.Bind<bool>("Misc", "ThumperNoThunder", true, "Thumpers no longer play thunder sound effects from their voice when they stop chasing after players.");
configSpaceMutesCruiser = ((BaseUnityPlugin)this).Config.Bind<CruiserMute>("Misc", "SpaceMutesCruiser", CruiserMute.NotRadio, "What audio sources should be muted on the Cruiser when in orbit. (Engine sounds, the horn, the radio, etc.)");
if (configFixMasks.Value)
{
if (((BaseUnityPlugin)this).Config.Bind<bool>("Misc", "DontFixMasks", false, "Legacy setting, use \"FixMasks\" instead").Value)
{
configFixMasks.Value = false;
}
((BaseUnityPlugin)this).Config.Remove(((BaseUnityPlugin)this).Config["Misc", "DontFixMasks"].Definition);
((BaseUnityPlugin)this).Config.Save();
}
new Harmony("butterystancakes.lethalcompany.enemysoundfixes").PatchAll();
Logger.LogInfo((object)"Enemy Sound Fixes v1.5.10 loaded");
}
}
internal class References
{
internal static readonly FieldInfo CREATURE_VOICE = AccessTools.Field(typeof(EnemyAI), "creatureVoice");
internal static readonly FieldInfo IS_ENEMY_DEAD = AccessTools.Field(typeof(EnemyAI), "isEnemyDead");
internal static readonly FieldInfo ENGINE_AUDIO_1 = AccessTools.Field(typeof(VehicleController), "engineAudio1");
internal static readonly MethodInfo REALTIME_SINCE_STARTUP = AccessTools.DeclaredPropertyGetter(typeof(Time), "realtimeSinceStartup");
internal static readonly MethodInfo PLAY_ONE_SHOT = AccessTools.Method(typeof(AudioSource), "PlayOneShot", new Type[1] { typeof(AudioClip) }, (Type[])null);
internal static readonly MethodInfo STOP = AccessTools.Method(typeof(AudioSource), "Stop", Array.Empty<Type>(), (Type[])null);
internal static readonly MethodInfo DAMAGE_PLAYER = AccessTools.Method(typeof(PlayerControllerB), "DamagePlayer", (Type[])null, (Type[])null);
internal static readonly MethodInfo HIT_ENEMY = AccessTools.Method(typeof(EnemyAI), "HitEnemy", (Type[])null, (Type[])null);
internal static readonly MethodInfo PLAY_RANDOM_CLIP = AccessTools.Method(typeof(RoundManager), "PlayRandomClip", (Type[])null, (Type[])null);
internal static AudioClip baboonTakeDamage;
internal static AudioClip hitEnemyBody;
}
public static class PluginInfo
{
public const string PLUGIN_GUID = "EnemySoundFixes";
public const string PLUGIN_NAME = "EnemySoundFixes";
public const string PLUGIN_VERSION = "1.5.10";
}
}
namespace EnemySoundFixes.Patches
{
[HarmonyPatch]
internal class BaboonHawkPatches
{
[HarmonyPatch(typeof(BaboonBirdAI), "HitEnemy")]
[HarmonyPostfix]
private static void BaboonBirdAIPostHitEnemy(BaboonBirdAI __instance, bool playHitSFX)
{
if (playHitSFX && !((EnemyAI)__instance).isEnemyDead)
{
if (!((EnemyAI)__instance).isEnemyDead && (Object)(object)References.baboonTakeDamage != (Object)null)
{
((EnemyAI)__instance).creatureVoice.PlayOneShot(References.baboonTakeDamage);
Plugin.Logger.LogDebug((object)"Baboon hawk: Ouch");
}
else if ((Object)(object)References.hitEnemyBody != (Object)null)
{
((EnemyAI)__instance).creatureSFX.PlayOneShot(References.hitEnemyBody);
}
}
}
[HarmonyPatch(typeof(BaboonBirdAI), "OnCollideWithEnemy")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> EnemyTransOnCollideWithEnemy(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
//IL_01b0: Expected O, but got Unknown
//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
//IL_01c2: Expected O, but got Unknown
//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
//IL_01d0: Expected O, but got Unknown
//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
//IL_01de: Expected O, but got Unknown
//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
//IL_00f7: Expected O, but got Unknown
//IL_0103: Unknown result type (might be due to invalid IL or missing references)
//IL_0109: Expected O, but got Unknown
//IL_0117: Unknown result type (might be due to invalid IL or missing references)
//IL_011d: Expected O, but got Unknown
List<CodeInstruction> list = instructions.ToList();
MethodInfo methodInfo = AccessTools.Method(typeof(Animator), "ResetTrigger", new Type[1] { typeof(string) }, (Type[])null);
MethodInfo methodInfo2 = AccessTools.DeclaredPropertyGetter(typeof(RoundManager), "Instance");
for (int i = 3; i < list.Count; i++)
{
if (list[i].opcode == OpCodes.Callvirt && (MethodInfo)list[i].operand == methodInfo)
{
for (int j = i + 1; j < list.Count; j++)
{
if (list[j].opcode == OpCodes.Call && (MethodInfo)list[j].operand == methodInfo2)
{
Label label = generator.DefineLabel();
list[j].labels.Add(label);
list.InsertRange(i - 3, new <>z__ReadOnlyArray<CodeInstruction>((CodeInstruction[])(object)new CodeInstruction[3]
{
new CodeInstruction(OpCodes.Ldarg_2, (object)null),
new CodeInstruction(OpCodes.Ldfld, (object)References.IS_ENEMY_DEAD),
new CodeInstruction(OpCodes.Brtrue, (object)label)
}));
Plugin.Logger.LogDebug((object)"Transpiler (Baboon hawk): Don't play hit sound when attacking dead enemy (A)");
i += 3;
break;
}
}
}
else if (list[i].opcode == OpCodes.Callvirt && (MethodInfo)list[i].operand == References.HIT_ENEMY)
{
list.RemoveAt(i - 2);
list.InsertRange(i - 2, new <>z__ReadOnlyArray<CodeInstruction>((CodeInstruction[])(object)new CodeInstruction[4]
{
new CodeInstruction(OpCodes.Ldarg_2, (object)null),
new CodeInstruction(OpCodes.Ldfld, (object)References.IS_ENEMY_DEAD),
new CodeInstruction(OpCodes.Ldc_I4_0, (object)null),
new CodeInstruction(OpCodes.Ceq, (object)null)
}));
Plugin.Logger.LogDebug((object)"Transpiler (Baboon hawk): Don't play hit sound when attacking dead enemy (B)");
i += 4;
}
}
return list;
}
}
[HarmonyPatch]
internal class BrackenPatches
{
[HarmonyPatch(typeof(FlowermanAI), "HitEnemy")]
[HarmonyPrefix]
private static void FlowermanAIPreHitEnemy(FlowermanAI __instance, int force, bool playHitSFX)
{
GeneralPatches.playHitSound = playHitSFX && !((EnemyAI)__instance).isEnemyDead && ((EnemyAI)__instance).enemyHP <= force;
}
[HarmonyPatch(typeof(FlowermanAI), "KillEnemy")]
[HarmonyPostfix]
private static void FlowermanAIPostKillEnemy(FlowermanAI __instance, bool destroy)
{
if (GeneralPatches.playHitSound)
{
GeneralPatches.playHitSound = false;
if (!destroy)
{
((EnemyAI)__instance).creatureSFX.PlayOneShot(((EnemyAI)__instance).enemyType.hitBodySFX);
Plugin.Logger.LogDebug((object)"Bracken: Play hit sound on death");
}
}
}
}
[HarmonyPatch]
internal class ButlerPatches
{
[HarmonyPatch(typeof(ButlerEnemyAI), "Update")]
[HarmonyPostfix]
private static void ButlerEnemyAIPostUpdate(ButlerEnemyAI __instance)
{
if (((EnemyAI)__instance).isEnemyDead && __instance.buzzingAmbience.isPlaying && ((EnemyAI)__instance).creatureAnimator.GetBool("popFinish"))
{
__instance.buzzingAmbience.Stop();
Plugin.Logger.LogDebug((object)"Butler: Stop buzzing (bugs are free)");
}
}
}
[HarmonyPatch]
internal class CruiserPatches
{
[HarmonyPatch(typeof(VehicleController), "RevCarClientRpc")]
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
[HarmonyPatch(typeof(VehicleController), "SetIgnition")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> VehicleControllerTransEngineRev(IEnumerable<CodeInstruction> instructions)
{
//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
//IL_00b3: Expected O, but got Unknown
//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
//IL_00c5: Expected O, but got Unknown
//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
//IL_00d7: Expected O, but got Unknown
List<CodeInstruction> list = instructions.ToList();
for (int i = 4; i < list.Count; i++)
{
if (list[i].opcode == OpCodes.Callvirt && (MethodInfo)list[i].operand == References.PLAY_ONE_SHOT && list[i - 3].opcode == OpCodes.Ldfld && (FieldInfo)list[i - 3].operand == References.ENGINE_AUDIO_1)
{
list.InsertRange(i - 4, new <>z__ReadOnlyArray<CodeInstruction>((CodeInstruction[])(object)new CodeInstruction[3]
{
new CodeInstruction(list[i - 4].opcode, list[i - 4].operand),
new CodeInstruction(OpCodes.Ldfld, (object)References.ENGINE_AUDIO_1),
new CodeInstruction(OpCodes.Callvirt, (object)References.STOP)
}));
Plugin.Logger.LogDebug((object)"Transpiler (Cruiser): Reset engine rev sounds");
return list;
}
}
Plugin.Logger.LogError((object)"Cruiser transpiler failed");
return list;
}
[HarmonyPatch(typeof(VehicleController), "PlayCollisionAudio")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> VehicleControllerTransPlayCollisionAudio(IEnumerable<CodeInstruction> instructions)
{
List<CodeInstruction> list = instructions.ToList();
FieldInfo fieldInfo = AccessTools.Field(typeof(VehicleController), "audio1Type");
FieldInfo operand = AccessTools.Field(typeof(VehicleController), "audio1Time");
FieldInfo fieldInfo2 = AccessTools.Field(typeof(VehicleController), "audio2Type");
FieldInfo operand2 = AccessTools.Field(typeof(VehicleController), "audio2Time");
for (int i = 0; i < list.Count - 2; i++)
{
if (list[i].opcode == OpCodes.Call && (MethodInfo)list[i].operand == References.REALTIME_SINCE_STARTUP && list[i + 2].opcode == OpCodes.Ldfld)
{
if ((FieldInfo)list[i + 2].operand == fieldInfo)
{
Plugin.Logger.LogDebug((object)"Transpiler (Cruiser): Fix timestamp check for collision audio (#1)");
list[i + 2].operand = operand;
}
else if ((FieldInfo)list[i + 2].operand == fieldInfo2)
{
Plugin.Logger.LogDebug((object)"Transpiler (Cruiser): Fix timestamp check for collision audio (#2)");
list[i + 2].operand = operand2;
}
}
}
return list;
}
[HarmonyPatch(typeof(VehicleController), "SetVehicleAudioProperties")]
[HarmonyPrefix]
private static void VehicleControllerPreSetVehicleAudioProperties(VehicleController __instance, AudioSource audio, ref bool audioActive)
{
if (audioActive && (((Object)(object)audio == (Object)(object)__instance.extremeStressAudio && __instance.magnetedToShip) || (((Object)(object)audio == (Object)(object)__instance.rollingAudio || (Object)(object)audio == (Object)(object)__instance.skiddingAudio) && (__instance.magnetedToShip || (!__instance.FrontLeftWheel.isGrounded && !__instance.FrontRightWheel.isGrounded && !__instance.BackLeftWheel.isGrounded && !__instance.FrontLeftWheel.isGrounded)))))
{
audioActive = false;
}
}
[HarmonyPatch(typeof(VehicleController), "SetVehicleAudioProperties")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> VehicleControllerTransSetVehicleAudioProperties(IEnumerable<CodeInstruction> instructions)
{
List<CodeInstruction> list = instructions.ToList();
MethodInfo methodInfo = AccessTools.DeclaredPropertyGetter(typeof(AudioSource), "volume");
for (int i = 0; i < list.Count - 2; i++)
{
if (list[i].opcode == OpCodes.Callvirt && (MethodInfo)list[i].operand == methodInfo && list[i + 1].opcode == OpCodes.Ldc_R4 && (float)list[i + 1].operand == 0f && list[i - 2].opcode == OpCodes.Bne_Un)
{
list[i + 1].operand = 0.001f;
list[i + 2].opcode = OpCodes.Bge;
Plugin.Logger.LogDebug((object)"Transpiler (Cruiser): Stop audio source when volume is close enough to zero");
break;
}
}
return list;
}
[HarmonyPatch(typeof(VehicleController), "LateUpdate")]
[HarmonyPostfix]
private static void VehicleControllerPostLateUpdate(VehicleController __instance)
{
if (__instance.magnetedToShip && Plugin.configSpaceMutesCruiser.Value > CruiserMute.Nothing && (StartOfRound.Instance.inShipPhase || !StartOfRound.Instance.shipDoorsEnabled))
{
__instance.hornAudio.mute = true;
__instance.engineAudio1.mute = true;
__instance.engineAudio2.mute = true;
__instance.turbulenceAudio.mute = true;
if (Plugin.configSpaceMutesCruiser.Value == CruiserMute.NotRadio)
{
__instance.radioAudio.mute = false;
__instance.radioInterference.mute = false;
}
else
{
__instance.radioAudio.mute = true;
__instance.radioInterference.mute = true;
}
}
else
{
__instance.hornAudio.mute = false;
__instance.engineAudio1.mute = false;
__instance.engineAudio2.mute = false;
__instance.turbulenceAudio.mute = false;
__instance.radioAudio.mute = false;
__instance.radioInterference.mute = false;
}
}
}
[HarmonyPatch]
internal class EyelessDogPatches
{
private const float TIME_DROP_CARRIED_BODY = 5.01f;
private static Dictionary<MouthDogAI, (float Pitch, float Time)> dogPitches = new Dictionary<MouthDogAI, (float, float)>();
[HarmonyPatch(typeof(MouthDogAI), "KillEnemy")]
[HarmonyPostfix]
private static void MouthDogAIPostKillEnemy(MouthDogAI __instance, bool destroy)
{
if (GeneralPatches.playHitSound)
{
GeneralPatches.playHitSound = false;
if (!destroy && (Object)(object)References.hitEnemyBody != (Object)null)
{
((EnemyAI)__instance).creatureSFX.PlayOneShot(((EnemyAI)__instance).enemyType.hitBodySFX);
Plugin.Logger.LogDebug((object)"Mouth dog: Play hit sound on death");
}
}
if (!destroy)
{
((EnemyAI)__instance).creatureVoice.mute = true;
Plugin.Logger.LogDebug((object)"Eyeless dog: Don't start breathing after death");
}
}
[HarmonyPatch(typeof(MouthDogAI), "Start")]
[HarmonyPostfix]
private static void MouthDogAIPostStart(MouthDogAI __instance)
{
Random random = new Random(StartOfRound.Instance.randomMapSeed + (int)((NetworkBehaviour)__instance).NetworkObjectId);
if (random.Next(10) < 2)
{
((EnemyAI)__instance).creatureVoice.pitch = 0.6f + 0.7f * (float)random.NextDouble();
}
else
{
((EnemyAI)__instance).creatureVoice.pitch = 0.9f + 0.2f * (float)random.NextDouble();
}
Plugin.Logger.LogDebug((object)"Eyeless dog: Reroll voice pitch (seeded random)");
}
[HarmonyPatch(typeof(MouthDogAI), "KillPlayerClientRpc")]
[HarmonyPrefix]
private static void MouthDogAIPreKillPlayerClientRpc(MouthDogAI __instance)
{
if (!dogPitches.ContainsKey(__instance))
{
dogPitches.Add(__instance, (((EnemyAI)__instance).creatureVoice.pitch, Time.timeSinceLevelLoad + 5.01f));
Plugin.Logger.LogDebug((object)$"Eyeless dog #{((Object)__instance).GetInstanceID()}: Cached {((EnemyAI)__instance).creatureVoice.pitch}x voice pitch (kill animation will start)");
}
else
{
Plugin.Logger.LogWarning((object)$"Eyeless dog #{((Object)__instance).GetInstanceID()}: Tried to initiate kill animation before ending previous kill animation");
}
}
[HarmonyPatch(typeof(MouthDogAI), "Update")]
[HarmonyPostfix]
private static void MouthDogAIPostUpdate(MouthDogAI __instance, bool ___inKillAnimation)
{
if (!((EnemyAI)__instance).isEnemyDead)
{
if (dogPitches.Count > 0 && !___inKillAnimation && dogPitches.TryGetValue(__instance, out (float, float) value) && Time.timeSinceLevelLoad >= value.Item2)
{
dogPitches.Remove(__instance);
Plugin.Logger.LogDebug((object)$"Eyeless dog #{((Object)__instance).GetInstanceID()}: Reset voice pitch now that kill sound is done ({((EnemyAI)__instance).creatureVoice.pitch}x -> {value.Item1}x)");
((EnemyAI)__instance).creatureVoice.pitch = value.Item1;
}
if (!((EnemyAI)__instance).creatureVoice.isPlaying)
{
((EnemyAI)__instance).creatureVoice.Play();
}
}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> MouthDogAITransEnterChaseMode(IEnumerable<CodeInstruction> instructions)
{
List<CodeInstruction> list = instructions.ToList();
for (int i = 1; i < list.Count; i++)
{
if (list[i].opcode == OpCodes.Callvirt && (MethodInfo)list[i].operand == References.PLAY_ONE_SHOT && list[i - 1].opcode == OpCodes.Ldfld && (FieldInfo)list[i - 1].operand == AccessTools.Field(typeof(MouthDogAI), "breathingSFX"))
{
for (int j = i - 4; j <= i; j++)
{
list[j].opcode = OpCodes.Nop;
}
Plugin.Logger.LogDebug((object)"Transpiler (Eyeless dog): Fix overlapping breathing");
break;
}
}
return list;
}
[HarmonyPatch(typeof(EnemyAI), "SubtractFromPowerLevel")]
[HarmonyPostfix]
private static void EnemyAIPostSubtractFromPowerLevel(EnemyAI __instance)
{
MouthDogAI val = (MouthDogAI)(object)((__instance is MouthDogAI) ? __instance : null);
if ((Object)(object)val != (Object)null && dogPitches.Remove(val))
{
Plugin.Logger.LogDebug((object)$"Eyeless dog #{((Object)__instance).GetInstanceID()}: Died mid kill animation (clean up cached reference)");
}
}
[HarmonyPatch(typeof(RoundManager), "ResetEnemyVariables")]
[HarmonyPostfix]
private static void RoundManagerPostResetEnemyVariables()
{
dogPitches.Clear();
}
[HarmonyPatch(typeof(MouthDogAI), "HitEnemy")]
[HarmonyPrefix]
private static void MouthDogAIPreHitEnemy(MouthDogAI __instance, int force, bool playHitSFX)
{
GeneralPatches.playHitSound = playHitSFX && !((EnemyAI)__instance).isEnemyDead && ((EnemyAI)__instance).enemyHP <= force;
}
}
[HarmonyPatch]
internal class ForestKeeperPatches
{
private const float TIME_PLAY_AUDIO_2 = 2.2f;
[HarmonyPatch(typeof(ForestGiantAI), "Update")]
[HarmonyPostfix]
private static void ForestGiantAIPostUpdate(ForestGiantAI __instance)
{
if (((EnemyAI)__instance).stunNormalizedTimer > 0f || ((EnemyAI)__instance).isEnemyDead || ((EnemyAI)__instance).currentBehaviourStateIndex == 2)
{
PlayAudioAnimationEvent component = ((Component)__instance.animationContainer).GetComponent<PlayAudioAnimationEvent>();
AudioSource audioToPlay = component.audioToPlay;
if (audioToPlay.isPlaying)
{
audioToPlay.Stop();
Plugin.Logger.LogDebug((object)"Forest keeper: Stop chewing (eating animation interrupted)");
}
ParticleSystem particle = component.particle;
if (particle.isEmitting)
{
particle.Stop();
Plugin.Logger.LogDebug((object)"Forest keeper: Stop spraying blood from mouth (eating animation interrupted)");
}
}
}
[HarmonyPatch(typeof(ForestGiantAI), "StopKillAnimation")]
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> ForestGiantAITransAnimation(IEnumerable<CodeInstruction> instructions)
{
List<CodeInstruction> list = instructions.ToList();
for (int i = 1; i < list.Count - 1; i++)
{
if (list[i].opcode == OpCodes.Ldfld && (FieldInfo)list[i].operand == References.CREATURE_VOICE)
{
for (int j = i - 1; j <= i + 1; j++)
{
list[j].opcode = OpCodes.Nop;
list[j].operand = null;
}
Plugin.Logger.LogDebug((object)"Transpiler (Forest Keeper): Don't interrupt voice");
break;
}
}
return list;
}
[HarmonyPatch(typeof(ForestGiantAI), "AnimationEventA")]
[HarmonyPostfix]
private static void ForestGiantAIPostAnimationEventA(ForestGiantAI __instance)
{
((EnemyAI)__instance).creatureSFX.PlayOneShot(__instance.giantFall);
Plugin.Logger.LogDebug((object)"Forest keeper: Fallen down");
}
[HarmonyPatch(typeof(PlayAudioAnimationEvent), "PlayAudio2")]
[HarmonyPrefix]
private static bool PlayAudioAnimationEventPrePlayAudio2(PlayAudioAnimationEvent __instance)
{
if (((Object)__instance.audioClip2).name == "FGiantEatPlayerSFX")
{
EnemyAI mainScript = ((Component)__instance).GetComponent<EnemyAnimationEvent>().mainScript;
ForestGiantAI val = (ForestGiantAI)(object)((mainScript is ForestGiantAI) ? mainScript : null);
if ((Object)(object)((EnemyAI)val).inSpecialAnimationWithPlayer != (Object)null && (Object)(object)((EnemyAI)val).inSpecialAnimationWithPlayer.inAnimationWithEnemy == (Object)(object)val)
{
__instance.audioToPlay.PlayOneShot(__instance.audioClip2);
Plugin.Logger.LogDebug((object)"Forest keeper: Play bite sound effect with overlap");
}
else
{
Plugin.Logger.LogDebug((object)"Forest keeper: Don't bite (player was teleported)");
}
return false;
}
return true;
}
[HarmonyPatch(typeof(PlayAudioAnimationEvent), "PlayParticle")]
[HarmonyPrefix]
private static bool PlayAudioAnimationEventPrePlayParticle(PlayAudioAnimationEvent __instance)
{
if ((Object)(object)__instance.audioClip2 != (Object)null && ((Object)__instance.audioClip2).name == "FGiantEatPlayerSFX")
{
EnemyAI mainScript = ((Component)__instance).GetComponent<EnemyAnimationEvent>().mainScript;
if ((Object)(object)mainScript.inSpecialAnimationWithPlayer == (Object)null || (Object)(object)mainScript.inSpecialAnimationWithPlayer.inAnimationWithEnemy != (Object)(object)mainScript)
{
Plugin.Logger.LogDebug((object)"Forest keeper: Don't spray blood (player was teleported)");
return false;
}
}
return true;
}
[HarmonyPatch(typeof(EnemyAI), "CancelSpecialAnimationWithPlayer")]
[HarmonyPrefix]
private static void EnemyAIPreCancelSpecialAnimationWithPlayer(EnemyAI __instance)
{
if (!(__instance is ForestGiantAI) || !((Object)(object)__instance.inSpecialAnimationWithPlayer != (Object)null) || !((Object)(object)__instance.inSpecialAnimationWithPlayer.inAnimationWithEnemy == (Object)(object)__instance) || __instance.inSpecialAnimationWithPlayer.isPlayerDead)
{
return;
}
PlayAudioAnimationEvent component = ((Component)((ForestGiantAI)((__instance is ForestGiantAI) ? __instance : null)).animationContainer).GetComponent<PlayAudioAnimationEvent>();
AudioSource audioToPlay = component.audioToPlay;
if (!audioToPlay.isPlaying)
{
return;
}
AudioClip clip = audioToPlay.clip;
if (((clip != null) ? ((Object)clip).name : null) == "Roar" && audioToPlay.time > 2.2f)
{
audioToPlay.Stop();
Plugin.Logger.LogDebug((object)"Forest keeper: Stop chewing (player was teleported)");
ParticleSystem particle = component.particle;
if (particle.isEmitting)
{
particle.Stop();
Plugin.Logger.LogDebug((object)"Forest keeper: Stop spraying blood from mouth (player was teleported)");
}
}
}
}
[HarmonyPatch]
internal class GeneralPatches
{
internal static bool playHitSound;
[HarmonyPatch(typeof(QuickMenuManager), "Start")]
[HarmonyPostfix]
private static void QuickMenuManagerPostStart(QuickMenuManager __instance)
{
List<SpawnableEnemyWithRarity> enemies = __instance.testAllEnemiesLevel.Enemies;
List<SpawnableEnemyWithRarity> outsideEnemies = __instance.testAllEnemiesLevel.OutsideEnemies;
List<SpawnableEnemyWithRarity> daytimeEnemies = __instance.testAllEnemiesLevel.DaytimeEnemies;
List<SpawnableEnemyWithRarity> list = new List<SpawnableEnemyWithRarity>(enemies.Count + outsideEnemies.Count + daytimeEnemies.Count);
list.AddRange(enemies);
list.AddRange(outsideEnemies);
list.AddRange(daytimeEnemies);
EnemyType val = null;
foreach (SpawnableEnemyWithRarity item in list)
{
switch (((Object)item.enemyType).name)
{
case "BaboonHawk":
if ((Object)(object)References.baboonTakeDamage == (Object)null)
{
References.baboonTakeDamage = item.enemyType.hitBodySFX;
Plugin.Logger.LogDebug((object)"Cached baboon hawk damage sound");
}
item.enemyType.hitBodySFX = null;
Plugin.Logger.LogDebug((object)"Overwritten baboon hawk damage sound");
((EnemyAI)item.enemyType.enemyPrefab.GetComponent<BaboonBirdAI>()).dieSFX = item.enemyType.deathSFX;
Plugin.Logger.LogDebug((object)"Overwritten missing baboon hawk death sound");
break;
case "Centipede":
((EnemyAI)item.enemyType.enemyPrefab.GetComponent<CentipedeAI>()).creatureSFX.loop = true;
Plugin.Logger.LogDebug((object)"Loop snare flea walking and clinging");
break;
case "Crawler":
if (Plugin.configThumperNoThunder.Value)
{
EnemyBehaviourState val2 = ((IEnumerable<EnemyBehaviourState>)((EnemyAI)item.enemyType.enemyPrefab.GetComponent<CrawlerAI>()).enemyBehaviourStates).FirstOrDefault((Func<EnemyBehaviourState, bool>)((EnemyBehaviourState enemyBehaviourState) => enemyBehaviourState.name == "searching"));
if (val2 != null)
{
val2.VoiceClip = null;
val2.playOneShotVoice = false;
Plugin.Logger.LogDebug((object)"Remove thunder sound from thumper");
}
}
break;
case "ForestGiant":
{
ForestGiantAI component = item.enemyType.enemyPrefab.GetComponent<ForestGiantAI>();
((EnemyAI)component).creatureSFX.spatialBlend = 1f;
Plugin.Logger.LogDebug((object)"Fix forest giant global audio volume");
item.enemyType.hitBodySFX = ((IEnumerable<FootstepSurface>)StartOfRound.Instance.footstepSurfaces).FirstOrDefault((Func<FootstepSurface, bool>)((FootstepSurface footstepSurface) => footstepSurface.surfaceTag == "Wood")).hitSurfaceSFX;
Plugin.Logger.LogDebug((object)"Overwritten missing forest giant hit sound");
component.giantBurningAudio.volume = 0f;
Plugin.Logger.LogDebug((object)"Fix forest giant burning volume fade");
break;
}
case "MouthDog":
val = item.enemyType;
break;
}
if ((Object)(object)References.hitEnemyBody == (Object)null && ((Object)item.enemyType.hitBodySFX).name == "HitEnemyBody")
{
References.hitEnemyBody = item.enemyType.hitBodySFX;
Plugin.Logger.LogDebug((object)"Cached generic damage sound");
}
}
if ((Object)(object)References.hitEnemyBody != (Object)null && (Object)(object)val != (Object)null)
{
val.hitBodySFX = References.hitEnemyBody;
Plugin.Logger.LogDebug((object)"Overwritten missing eyeless dog hit sound");
}
}
[HarmonyPatch(typeof(AnimatedObjectTrigger), "Start")]
[HarmonyPostfix]
private static void AnimatedObjectTriggerPostStart(AnimatedObjectTrigger __instance)
{
if (__instance.playAudiosInSequence && (Object)(object)__instance.playParticle == (Object)null)
{
__instance.playParticleOnTimesTriggered = -1;
Plugin.Logger.LogDebug((object)("\"" + ((Object)__instance).name + ".AnimatedObjectTrigger\" doesn't have particles attached"));
}
}
[HarmonyPatch(typeof(MouthDogAI), "OnCollideWithEnemy")]
[HarmonyPatch(typeof(BushWolfEnemy), "OnCollideWithEnemy")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> EnemyTransOnCollideWithEnemy(IEnumerable<CodeInstruction> instructions)
{
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Expected O, but got Unknown
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_0079: Expected O, but got Unknown
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0087: Expected O, but got Unknown
//IL_008f: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: Expected O, but got Unknown
List<CodeInstruction> list = instructions.ToList();
for (int i = 2; i < list.Count; i++)
{
if (list[i].opcode == OpCodes.Callvirt && (MethodInfo)list[i].operand == References.HIT_ENEMY)
{
list.RemoveAt(i - 2);
list.InsertRange(i - 2, new <>z__ReadOnlyArray<CodeInstruction>((CodeInstruction[])(object)new CodeInstruction[4]
{
new CodeInstruction(OpCodes.Ldarg_2, (object)null),
new CodeInstruction(OpCodes.Ldfld, (object)References.IS_ENEMY_DEAD),
new CodeInstruction(OpCodes.Ldc_I4_0, (object)null),
new CodeInstruction(OpCodes.Ceq, (object)null)
}));
Plugin.Logger.LogDebug((object)"Transpiler: Don't play hit sound when attacking dead enemy");
break;
}
}
return list;
}
[HarmonyPatch(typeof(StormyWeather), "PlayThunderEffects")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> TransPlayThunderEffects(IEnumerable<CodeInstruction> instructions)
{
List<CodeInstruction> list = instructions.ToList();
FieldInfo fieldInfo = AccessTools.Field(typeof(StartOfRound), "shipCreakSFX");
for (int i = 5; i < list.Count; i++)
{
if (list[i].opcode == OpCodes.Call && (MethodInfo)list[i].operand == References.PLAY_RANDOM_CLIP && list[i - 1].opcode == OpCodes.Ldc_I4 && (int)list[i - 1].operand == 1000 && list[i - 5].opcode == OpCodes.Ldfld && (FieldInfo)list[i - 5].operand == fieldInfo)
{
list[i - 1].opcode = OpCodes.Ldc_I4_6;
Plugin.Logger.LogDebug((object)"Transpiler (Stormy weather): No \"Hey\" when ship is struck");
break;
}
}
return list;
}
}
[HarmonyPatch]
internal class HoardingBugPatches
{
[HarmonyPatch(typeof(HoarderBugAI), "KillEnemy")]
[HarmonyPostfix]
private static void HoarderBugAIPostKillEnemy(HoarderBugAI __instance, bool destroy)
{
if (GeneralPatches.playHitSound)
{
GeneralPatches.playHitSound = false;
if (!destroy)
{
((EnemyAI)__instance).creatureSFX.PlayOneShot(((EnemyAI)__instance).enemyType.hitBodySFX);
Plugin.Logger.LogDebug((object)"Hoarding bug: Play hit sound on death");
}
}
if (!destroy)
{
((EnemyAI)__instance).creatureVoice.PlayOneShot(((EnemyAI)__instance).enemyType.deathSFX);
Plugin.Logger.LogDebug((object)"Hoarding bug: Played backup death sound");
}
}
[HarmonyPatch(typeof(HoarderBugAI), "HitEnemy")]
[HarmonyPrefix]
private static void HoarderBugAIPreHitEnemy(HoarderBugAI __instance, int force, bool playHitSFX)
{
GeneralPatches.playHitSound = playHitSFX && !((EnemyAI)__instance).isEnemyDead && ((EnemyAI)__instance).enemyHP <= force;
}
}
[HarmonyPatch]
internal class KidnapperFoxPatches
{
[HarmonyPatch(typeof(BushWolfEnemy), "HitTongueLocalClient")]
[HarmonyPostfix]
private static void BushWolfEnemyPostHitTongueLocalClient(BushWolfEnemy __instance)
{
((EnemyAI)__instance).creatureVoice.PlayOneShot(__instance.hitBushWolfSFX);
Plugin.Logger.LogDebug((object)"Kidnapper fox: Bit my tongue");
}
[HarmonyPatch(typeof(BushWolfEnemy), "Update")]
[HarmonyPostfix]
private static void BushWolfEnemyPostUpdate(BushWolfEnemy __instance, bool ___dragging)
{
if ((!___dragging || ((EnemyAI)__instance).isEnemyDead || ((EnemyAI)__instance).stunNormalizedTimer > 0f) && ((EnemyAI)__instance).creatureVoice.isPlaying && (Object)(object)((EnemyAI)__instance).creatureVoice.clip == (Object)(object)__instance.snarlSFX)
{
((EnemyAI)__instance).creatureVoice.clip = null;
Plugin.Logger.LogDebug((object)"Kidnapper fox: Cancel snarl (failsafe)");
}
if (((EnemyAI)__instance).isEnemyDead && __instance.spitParticle.isEmitting)
{
__instance.spitParticle.Stop();
Plugin.Logger.LogDebug((object)"Kidnapper fox: Cancel drool");
}
}
[HarmonyPatch(typeof(BushWolfEnemy), "CancelReelingPlayerIn")]
[HarmonyPrefix]
private static void BushWolfEnemyPreCancelReelingPlayerIn(BushWolfEnemy __instance, ref bool ___dragging)
{
if (___dragging && ((EnemyAI)__instance).isEnemyDead)
{
___dragging = false;
Plugin.Logger.LogDebug((object)"Kidnapper fox: Don't let dragging interrupt death voice");
}
}
[HarmonyPatch(typeof(BushWolfEnemy), "HitEnemy")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> BushWolfEnemyTransHitEnemy(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
//IL_008f: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: Expected O, but got Unknown
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Expected O, but got Unknown
//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
//IL_00ba: Expected O, but got Unknown
List<CodeInstruction> list = instructions.ToList();
MethodInfo methodInfo = AccessTools.Method(typeof(BushWolfEnemy), "CancelReelingPlayerIn", (Type[])null, (Type[])null);
Label label = generator.DefineLabel();
list[list.Count - 1].labels.Add(label);
for (int num = list.Count - 1; num >= 0; num--)
{
if (list[num].opcode == OpCodes.Call && (MethodInfo)list[num].operand == methodInfo)
{
list.InsertRange(num + 1, new <>z__ReadOnlyArray<CodeInstruction>((CodeInstruction[])(object)new CodeInstruction[3]
{
new CodeInstruction(OpCodes.Ldarg_0, (object)null),
new CodeInstruction(OpCodes.Ldfld, (object)References.IS_ENEMY_DEAD),
new CodeInstruction(OpCodes.Brtrue, (object)label)
}));
Plugin.Logger.LogDebug((object)"Transpiler (Kidnapper fox): Don't cry when dead");
break;
}
}
return list;
}
}
[HarmonyPatch]
internal class ManeaterPatches
{
[HarmonyPatch(typeof(CaveDwellerAI), "HitEnemy")]
[HarmonyPrefix]
private static void CaveDwellerAIPreHitEnemy(CaveDwellerAI __instance, int force, bool playHitSFX)
{
GeneralPatches.playHitSound = playHitSFX && !((EnemyAI)__instance).isEnemyDead && ((EnemyAI)__instance).enemyHP <= 1;
}
[HarmonyPatch(typeof(CaveDwellerAI), "KillEnemy")]
[HarmonyPostfix]
private static void CaveDwellerAIPostKillEnemy(CaveDwellerAI __instance, bool destroy)
{
if (destroy)
{
return;
}
if (GeneralPatches.playHitSound)
{
GeneralPatches.playHitSound = false;
if (!destroy)
{
((EnemyAI)__instance).creatureSFX.Stop();
((EnemyAI)__instance).creatureSFX.PlayOneShot(((EnemyAI)__instance).enemyType.hitBodySFX);
Plugin.Logger.LogDebug((object)"Maneater: Play hit sound on death");
}
}
AudioSource[] array = (AudioSource[])(object)new AudioSource[5] { __instance.clickingAudio1, __instance.clickingAudio2, __instance.walkingAudio, __instance.screamAudio, __instance.screamAudioNonDiagetic };
foreach (AudioSource obj in array)
{
obj.Stop();
obj.mute = true;
}
((EnemyAI)__instance).creatureVoice.Stop();
((EnemyAI)__instance).creatureVoice.PlayOneShot(((EnemyAI)__instance).dieSFX);
Plugin.Logger.LogDebug((object)"Maneater: Played backup death sound");
}
}
[HarmonyPatch]
internal class MaskPatches
{
[HarmonyPatch(typeof(RandomPeriodicAudioPlayer), "Update")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> RandomPeriodicAudioPlayerTransUpdate(IEnumerable<CodeInstruction> instructions)
{
if (!Plugin.configFixMasks.Value)
{
return instructions;
}
List<CodeInstruction> list = instructions.ToList();
for (int i = 1; i < list.Count; i++)
{
if (!(list[i].opcode == OpCodes.Add))
{
continue;
}
for (int num = i - 1; num >= 0; num--)
{
if (list[num].opcode == OpCodes.Call && (MethodInfo)list[num].operand == References.REALTIME_SINCE_STARTUP)
{
list[i].opcode = OpCodes.Nop;
list[num].opcode = OpCodes.Nop;
Plugin.Logger.LogDebug((object)"Transpiler (Periodic mask audio): Fix intervals");
return list;
}
}
}
return list;
}
[HarmonyPatch(typeof(MaskedPlayerEnemy), "Start")]
[HarmonyPostfix]
private static void MaskedPlayerEnemyPostStart(MaskedPlayerEnemy __instance)
{
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
if (!Plugin.configBetterMimicSteps.Value)
{
return;
}
AudioSource val = GameNetworkManager.Instance?.localPlayerController?.movementAudio;
if ((Object)(object)val == (Object)null)
{
return;
}
((Component)__instance.movementAudio).transform.localPosition = new Vector3(0f, 0.278f, 0f);
__instance.movementAudio.volume = val.volume;
__instance.movementAudio.dopplerLevel = val.dopplerLevel;
__instance.movementAudio.spread = val.spread;
__instance.movementAudio.rolloffMode = (AudioRolloffMode)2;
foreach (AudioSourceCurveType value in Enum.GetValues(typeof(AudioSourceCurveType)))
{
__instance.movementAudio.SetCustomCurve(value, val.GetCustomCurve(value));
}
Plugin.Logger.LogDebug((object)"Mimic: Footsteps match players");
}
[HarmonyPatch(typeof(MaskedPlayerEnemy), "HitEnemy")]
[HarmonyPrefix]
private static void MaskedPlayerEnemyPreHitEnemy(MaskedPlayerEnemy __instance, int force, bool playHitSFX)
{
GeneralPatches.playHitSound = playHitSFX && !((EnemyAI)__instance).isEnemyDead && ((EnemyAI)__instance).enemyHP <= force;
}
[HarmonyPatch(typeof(MaskedPlayerEnemy), "KillEnemy")]
[HarmonyPostfix]
private static void MaskedPlayerEnemyPostKillEnemy(MaskedPlayerEnemy __instance, bool destroy)
{
if (GeneralPatches.playHitSound)
{
GeneralPatches.playHitSound = false;
if (!destroy)
{
((EnemyAI)__instance).creatureSFX.PlayOneShot(((EnemyAI)__instance).enemyType.hitBodySFX);
Plugin.Logger.LogDebug((object)"Mimic: Play hit sound on death");
}
}
}
}
[HarmonyPatch]
internal class NutcrackerPatches
{
[HarmonyPatch(typeof(NutcrackerEnemyAI), "KillEnemy")]
[HarmonyPostfix]
private static void NutcrackerEnemyAIPostKillEnemy(NutcrackerEnemyAI __instance, bool destroy)
{
if (!destroy)
{
((EnemyAI)__instance).creatureVoice.loop = false;
((EnemyAI)__instance).creatureVoice.clip = null;
((EnemyAI)__instance).creatureVoice.pitch = 1f;
((EnemyAI)__instance).creatureVoice.PlayOneShot(((EnemyAI)__instance).enemyType.deathSFX);
Plugin.Logger.LogDebug((object)"Nutcracker: Played death sound");
}
}
}
[HarmonyPatch]
internal class PlayerPatches
{
[HarmonyPatch(typeof(PlayerControllerB), "DamagePlayer")]
[HarmonyPrefix]
private static void PlayerControllerBPreDamagePlayer(CauseOfDeath causeOfDeath, ref bool fallDamage)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Invalid comparison between Unknown and I4
if ((int)causeOfDeath == 2 && !fallDamage)
{
fallDamage = true;
Plugin.Logger.LogDebug((object)"Player: Treat Gravity damage as fall damage");
}
}
[HarmonyPatch(typeof(PlayerControllerB), "DamagePlayerFromOtherClientClientRpc")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> PlayerControllerBTransDamagePlayerFromOtherClientClientRpc(IEnumerable<CodeInstruction> instructions)
{
List<CodeInstruction> list = instructions.ToList();
for (int i = 3; i < list.Count; i++)
{
if (!(list[i].opcode == OpCodes.Call) || !((MethodInfo)list[i].operand == References.DAMAGE_PLAYER))
{
continue;
}
for (int num = i - 1; num > 0; num--)
{
if (list[num].opcode == OpCodes.Ldarg_1 && list[num + 1].opcode == OpCodes.Ldc_I4_1)
{
list[num + 1].opcode = OpCodes.Ldc_I4_0;
Plugin.Logger.LogDebug((object)"Transpiler (Players): Melee weapons don't stack hit sounds");
break;
}
}
}
return list;
}
}
[HarmonyPatch]
internal class SnareFleaPatches
{
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> TransDelayedShriek(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
//IL_0091: Unknown result type (might be due to invalid IL or missing references)
//IL_0097: Expected O, but got Unknown
//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
//IL_00a9: Expected O, but got Unknown
//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
//IL_00bc: Expected O, but got Unknown
//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
//IL_00ca: Expected O, but got Unknown
//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
//IL_00d8: Expected O, but got Unknown
List<CodeInstruction> list = instructions.ToList();
Label label = generator.DefineLabel();
for (int i = 0; i < list.Count - 1; i++)
{
if (list[i].opcode == OpCodes.Ldloc_1 && list[i + 1].opcode == OpCodes.Ldfld && (FieldInfo)list[i + 1].operand == References.CREATURE_VOICE)
{
list[i].labels.Add(label);
list.InsertRange(i, new <>z__ReadOnlyArray<CodeInstruction>((CodeInstruction[])(object)new CodeInstruction[5]
{
new CodeInstruction(OpCodes.Ldloc_1, (object)null),
new CodeInstruction(OpCodes.Ldfld, (object)References.IS_ENEMY_DEAD),
new CodeInstruction(OpCodes.Brfalse, (object)label),
new CodeInstruction(OpCodes.Ldc_I4_0, (object)null),
new CodeInstruction(OpCodes.Ret, (object)null)
}));
Plugin.Logger.LogDebug((object)"Transpiler (Snare flea): Don't shriek when dead (A)");
break;
}
}
return list;
}
[HarmonyPatch(typeof(CentipedeAI), "Update")]
[HarmonyPrefix]
private static void CentipedeAIPreUpdate(CentipedeAI __instance)
{
if (((EnemyAI)__instance).creatureSFX.isPlaying && (Object)(object)((EnemyAI)__instance).creatureSFX.clip == (Object)(object)((EnemyAI)__instance).enemyBehaviourStates[2].SFXClip && (((EnemyAI)__instance).isEnemyDead || ((EnemyAI)__instance).currentBehaviourStateIndex != 2))
{
((EnemyAI)__instance).creatureSFX.Stop();
((EnemyAI)__instance).creatureSFX.clip = null;
Plugin.Logger.LogDebug((object)"Snare flea: Stop walking while dead, clinging to player, or sneaking away");
}
}
[HarmonyPatch(typeof(CentipedeAI), "KillEnemy")]
[HarmonyPostfix]
private static void CentipedeAIPostKillEnemy(CentipedeAI __instance)
{
((EnemyAI)__instance).creatureSFX.clip = null;
}
[HarmonyPatch(typeof(EnemyAI), "PlayAudioOfCurrentState")]
[HarmonyPostfix]
private static void PostPlayAudioOfCurrentState(EnemyAI __instance)
{
if (__instance is CentipedeAI && __instance.currentBehaviourStateIndex == 1 && __instance.creatureVoice.pitch > 1f)
{
__instance.creatureVoice.pitch = 1f;
Plugin.Logger.LogDebug((object)"Snare flea: Reset \"voice\" pitch for attacking again");
}
}
[HarmonyPatch(typeof(CentipedeAI), "HitEnemy")]
[HarmonyPrefix]
private static void CentipedeAIPreHitEnemy(CentipedeAI __instance)
{
if (((EnemyAI)__instance).creatureSFX.isPlaying && (Object)(object)((EnemyAI)__instance).creatureSFX.clip == (Object)(object)((EnemyAI)__instance).enemyBehaviourStates[2].SFXClip)
{
((EnemyAI)__instance).creatureSFX.Stop();
((EnemyAI)__instance).creatureSFX.clip = null;
}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> TransFallFromCeiling(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00c9: Expected O, but got Unknown
//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Expected O, but got Unknown
//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
//IL_00ee: Expected O, but got Unknown
List<CodeInstruction> list = instructions.ToList();
Label label = generator.DefineLabel();
FieldInfo fieldInfo = AccessTools.Field(typeof(CentipedeAI), "shriekClips");
for (int i = 8; i < list.Count - 2; i++)
{
if (list[i].opcode == OpCodes.Call && (MethodInfo)list[i].operand == References.PLAY_RANDOM_CLIP && list[i - 5].opcode == OpCodes.Ldfld && (FieldInfo)list[i - 5].operand == fieldInfo)
{
list[i + 2].labels.Add(label);
list.InsertRange(i - 8, new <>z__ReadOnlyArray<CodeInstruction>((CodeInstruction[])(object)new CodeInstruction[3]
{
new CodeInstruction(OpCodes.Ldloc_1, (object)null),
new CodeInstruction(OpCodes.Ldfld, (object)References.IS_ENEMY_DEAD),
new CodeInstruction(OpCodes.Brtrue, (object)label)
}));
Plugin.Logger.LogDebug((object)"Transpiler (Snare flea): Don't shriek when dead (B)");
break;
}
}
return list;
}
}
[HarmonyPatch]
internal class TulipSnakePatches
{
[HarmonyPatch(typeof(FlowerSnakeEnemy), "Update")]
[HarmonyPostfix]
private static void FlowerSnakeEnemyPostUpdate(FlowerSnakeEnemy __instance)
{
if (!__instance.flappingAudio.isPlaying)
{
return;
}
if (((EnemyAI)__instance).isEnemyDead)
{
__instance.flappingAudio.Stop();
__instance.flappingAudio.mute = true;
Plugin.Logger.LogDebug((object)"Tulip snake: Stop making noise while dead");
}
else if ((Object)(object)__instance.flappingAudio.clip == (Object)(object)((EnemyAI)__instance).enemyType.audioClips[9])
{
if ((Object)(object)__instance.clingingToPlayer != (Object)null)
{
__instance.flappingAudio.Stop();
Plugin.Logger.LogDebug((object)"Tulip snake: Stop scurrying (latched to player)");
}
}
else if ((Object)(object)__instance.clingingToPlayer == (Object)null)
{
__instance.flappingAudio.Stop();
Plugin.Logger.LogDebug((object)"Tulip snake: Stop flapping (no longer clinging)");
}
}
[HarmonyPatch(typeof(FlowerSnakeEnemy), "StopLeapOnLocalClient")]
[HarmonyPostfix]
private static void PostStopLeapOnLocalClient(FlowerSnakeEnemy __instance, bool landOnGround)
{
if (landOnGround && !((EnemyAI)__instance).isEnemyDead)
{
__instance.flappingAudio.pitch = Random.Range(0.8f, 1.2f);
Plugin.Logger.LogDebug((object)"Tulip snake: Reroll scurry pitch (landed from leap)");
}
}
[HarmonyPatch(typeof(FlowerSnakeEnemy), "StopClingingOnLocalClient")]
[HarmonyPostfix]
private static void PostStopClingingOnLocalClient(FlowerSnakeEnemy __instance)
{
if (!((EnemyAI)__instance).isEnemyDead)
{
__instance.flappingAudio.pitch = Random.Range(0.8f, 1.2f);
Plugin.Logger.LogDebug((object)"Tulip snake: Reroll scurry pitch (dismounted player)");
}
}
[HarmonyPatch(typeof(FlowerSnakeEnemy), "HitEnemy")]
[HarmonyPrefix]
private static void FlowerSnakeEnemyPreHitEnemy(FlowerSnakeEnemy __instance, bool playHitSFX)
{
GeneralPatches.playHitSound = playHitSFX && !((EnemyAI)__instance).isEnemyDead;
}
[HarmonyPatch(typeof(FlowerSnakeEnemy), "KillEnemy")]
[HarmonyPostfix]
private static void FlowerSnakeEnemyPostKillEnemy(FlowerSnakeEnemy __instance, bool destroy)
{
if (GeneralPatches.playHitSound)
{
GeneralPatches.playHitSound = false;
if (!destroy && (Object)(object)References.hitEnemyBody != (Object)null)
{
((EnemyAI)__instance).creatureSFX.PlayOneShot(References.hitEnemyBody);
Plugin.Logger.LogDebug((object)"Tulip snake: Squish");
}
}
}
}
}
internal sealed class <>z__ReadOnlyArray<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>
{
int ICollection.Count => _items.Length;
bool ICollection.IsSynchronized => false;
object ICollection.SyncRoot => this;
object IList.this[int index]
{
get
{
return _items[index];
}
set
{
throw new NotSupportedException();
}
}
bool IList.IsFixedSize => true;
bool IList.IsReadOnly => true;
int IReadOnlyCollection<T>.Count => _items.Length;
T IReadOnlyList<T>.this[int index] => _items[index];
int ICollection<T>.Count => _items.Length;
bool ICollection<T>.IsReadOnly => true;
T IList<T>.this[int index]
{
get
{
return _items[index];
}
set
{
throw new NotSupportedException();
}
}
public <>z__ReadOnlyArray(T[] items)
{
_items = items;
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_items).GetEnumerator();
}
void ICollection.CopyTo(Array array, int index)
{
((ICollection)_items).CopyTo(array, index);
}
int IList.Add(object value)
{
throw new NotSupportedException();
}
void IList.Clear()
{
throw new NotSupportedException();
}
bool IList.Contains(object value)
{
return ((IList)_items).Contains(value);
}
int IList.IndexOf(object value)
{
return ((IList)_items).IndexOf(value);
}
void IList.Insert(int index, object value)
{
throw new NotSupportedException();
}
void IList.Remove(object value)
{
throw new NotSupportedException();
}
void IList.RemoveAt(int index)
{
throw new NotSupportedException();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return ((IEnumerable<T>)_items).GetEnumerator();
}
void ICollection<T>.Add(T item)
{
throw new NotSupportedException();
}
void ICollection<T>.Clear()
{
throw new NotSupportedException();
}
bool ICollection<T>.Contains(T item)
{
return ((ICollection<T>)_items).Contains(item);
}
void ICollection<T>.CopyTo(T[] array, int arrayIndex)
{
((ICollection<T>)_items).CopyTo(array, arrayIndex);
}
bool ICollection<T>.Remove(T item)
{
throw new NotSupportedException();
}
int IList<T>.IndexOf(T item)
{
return ((IList<T>)_items).IndexOf(item);
}
void IList<T>.Insert(int index, T item)
{
throw new NotSupportedException();
}
void IList<T>.RemoveAt(int index)
{
throw new NotSupportedException();
}
}