Decompiled source of BarberFixes v1.2.2

BarberFixes.dll

Decompiled 2 weeks ago
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.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Events;

[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("BarberFixes")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Fixes some major issues with Barbers")]
[assembly: AssemblyFileVersion("1.2.2.0")]
[assembly: AssemblyInformationalVersion("1.2.2+d764d486a15fc21dd6b3b468176decd79cde8d67")]
[assembly: AssemblyProduct("BarberFixes")]
[assembly: AssemblyTitle("BarberFixes")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.2.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 BarberFixes
{
	[BepInPlugin("butterystancakes.lethalcompany.barberfixes", "Barber Fixes", "1.2.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private const string PLUGIN_GUID = "butterystancakes.lethalcompany.barberfixes";

		private const string PLUGIN_NAME = "Barber Fixes";

		private const string PLUGIN_VERSION = "1.2.2";

		private const string VENT_SPAWN_FIX = "butterystancakes.lethalcompany.ventspawnfix";

		internal static ManualLogSource Logger;

		internal static ConfigEntry<bool> configSpawnInPairs;

		internal static ConfigEntry<bool> configDrumrollFromAll;

		internal static ConfigEntry<bool> configApplySpawningSettings;

		internal static ConfigEntry<bool> configOnlyOneBarber;

		internal static bool CAN_SPAWN_IN_GROUPS;

		private void Awake()
		{
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			Logger = ((BaseUnityPlugin)this).Logger;
			if (Chainloader.PluginInfos.ContainsKey("butterystancakes.lethalcompany.ventspawnfix"))
			{
				CAN_SPAWN_IN_GROUPS = true;
				Logger.LogInfo((object)"CROSS-COMPATIBILITY - VentSpawnFix detected");
			}
			configApplySpawningSettings = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawning", "ApplySpawningSettings", false, "The rest of the \"Spawning\" section's settings are only applied if this is enabled. You should disable this if you are using something else to configure enemy variables! (i.e. Clay Surgeon Overhaul, Lethal Quantities)");
			configOnlyOneBarber = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawning", "OnlyOneBarber", true, "(Host only) Only allow 1 Barber to spawn each day. Disabling this will raise the limit to 8 per day, as it was before v62.");
			configSpawnInPairs = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawning", "SpawnInPairs", false, "(Host only) Spawns Barbers in groups of 2, like in beta v55. This does nothing when \"OnlyOneBarber\" is enabled.\nNOTE: This REQUIRES VentSpawnFix to work!");
			configDrumrollFromAll = ((BaseUnityPlugin)this).Config.Bind<bool>("Music", "DrumrollFromAll", false, "If true, all Barbers will play the drumroll audio before they \"jump\". If false, only the master Barber will drumroll.\nThis is false in vanilla, although whether that's by design or a bug is unclear.");
			new Harmony("butterystancakes.lethalcompany.barberfixes").PatchAll();
			Logger.LogInfo((object)"Barber Fixes v1.2.2 loaded");
		}
	}
	[HarmonyPatch]
	internal class BarberFixesPatches
	{
		private static readonly MethodInfo OBJECT_DESTROY = AccessTools.Method(typeof(Object), "Destroy", new Type[1] { typeof(Object) }, (Type[])null);

		private static readonly FieldInfo MUSIC_AUDIO_2 = AccessTools.Field(typeof(ClaySurgeonAI), "musicAudio2");

		private static readonly FieldInfo ON_HOUR_CHANGED = AccessTools.Field(typeof(TimeOfDay), "onHourChanged");

		[HarmonyPatch(typeof(ClaySurgeonAI), "ChooseMasterSurgeon")]
		[HarmonyPrefix]
		private static bool PreChooseMasterSurgeon(ClaySurgeonAI __instance, ref bool ___isMaster)
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			if (!((NetworkBehaviour)__instance).IsServer)
			{
				return false;
			}
			ClaySurgeonAI[] array = Object.FindObjectsOfType<ClaySurgeonAI>();
			if (array.Length == 1)
			{
				__instance.master = __instance;
			}
			else
			{
				__instance.master = ((IEnumerable<ClaySurgeonAI>)array).FirstOrDefault((Func<ClaySurgeonAI, bool>)((ClaySurgeonAI barber) => (Object)(object)barber.master == (Object)(object)barber));
				if ((Object)(object)__instance.master == (Object)null)
				{
					__instance.master = __instance;
				}
			}
			if ((Object)(object)__instance.master == (Object)(object)__instance)
			{
				___isMaster = true;
				DanceClock.Start(__instance.startingInterval, __instance.endingInterval);
			}
			__instance.master.SendDanceBeat = new SimpleEvent();
			for (int i = 0; i < array.Length; i++)
			{
				if (!__instance.master.allClaySurgeons.Contains(array[i]))
				{
					__instance.master.allClaySurgeons.Add(array[i]);
				}
				if ((Object)(object)array[i] != (Object)(object)__instance.master)
				{
					array[i].master = __instance.master;
					array[i].ListenToMasterSurgeon();
				}
			}
			__instance.master.SyncMasterClaySurgeonClientRpc();
			return false;
		}

		[HarmonyPatch(typeof(ClaySurgeonAI), "ListenToMasterSurgeon")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> TransListenToMasterSurgeon(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = instructions.ToList();
			Label? label = null;
			for (int i = 0; i < list.Count; i++)
			{
				if (!label.HasValue)
				{
					if (list[i].opcode == OpCodes.Brtrue)
					{
						label = (Label)list[i].operand;
						Plugin.Logger.LogDebug((object)"Transpiler (ListenToMasterSurgeon): Allow when \"listeningToMasterSurgeon\" is already true");
					}
					list.RemoveAt(i--);
					continue;
				}
				if (list[i].opcode == OpCodes.Call && (MethodInfo)list[i].operand == OBJECT_DESTROY && list[i - 2].opcode == OpCodes.Ldfld && (FieldInfo)list[i - 2].operand == MUSIC_AUDIO_2)
				{
					list.RemoveRange(i - 3, 4);
					Plugin.Logger.LogDebug((object)"Transpiler (ListenToMasterSurgeon): Don't destroy \"musicAudio2\" (causes NRE)");
					i -= 3;
				}
				if (list[i].labels.Contains(label.Value))
				{
					list[i].labels.Remove(label.Value);
				}
			}
			return list;
		}

		[HarmonyPatch(typeof(ClaySurgeonAI), "SyncMasterClaySurgeonClientRpc")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> TransSyncMasterClaySurgeonClientRpc(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Expected O, but got Unknown
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Expected O, but got Unknown
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Expected O, but got Unknown
			List<CodeInstruction> list = instructions.ToList();
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].opcode == OpCodes.Callvirt && list[i].operand.ToString().Contains("AddListener") && list[i - 4].opcode == OpCodes.Ldfld && (FieldInfo)list[i - 4].operand == ON_HOUR_CHANGED)
				{
					list.RemoveRange(i - 5, 6);
					Plugin.Logger.LogDebug((object)"Transpiler (SyncMasterClaySurgeonClientRpc): Don't add listener to \"onHourChanged\"");
					i -= 5;
					list.InsertRange(i, new <>z__ReadOnlyArray<CodeInstruction>((CodeInstruction[])(object)new CodeInstruction[5]
					{
						new CodeInstruction(OpCodes.Ldarg_0, (object)null),
						new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(ClaySurgeonAI), "startingInterval")),
						new CodeInstruction(OpCodes.Ldarg_0, (object)null),
						new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(ClaySurgeonAI), "endingInterval")),
						new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(DanceClock), "Start", (Type[])null, (Type[])null))
					}));
					Plugin.Logger.LogDebug((object)"Transpiler (SyncMasterClaySurgeonClientRpc): Use new \"DanceClock\"");
				}
				else if (list[i].opcode == OpCodes.Call && (MethodInfo)list[i].operand == OBJECT_DESTROY && list[i - 2].opcode == OpCodes.Ldfld && (FieldInfo)list[i - 2].operand == MUSIC_AUDIO_2)
				{
					list.RemoveRange(i - 6, 7);
					Plugin.Logger.LogDebug((object)"Transpiler (SyncMasterClaySurgeonClientRpc): Don't destroy \"musicAudio2\" (causes NRE)");
					i -= 6;
				}
			}
			return list;
		}

		[HarmonyPatch(typeof(ClaySurgeonAI), "OnDestroy")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> ClaySurgeonAITransOnDestroy(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Expected O, but got Unknown
			List<CodeInstruction> list = instructions.ToList();
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].opcode == OpCodes.Callvirt && list[i].operand.ToString().Contains("RemoveListener") && list[i - 4].opcode == OpCodes.Ldfld && (FieldInfo)list[i - 4].operand == ON_HOUR_CHANGED)
				{
					list.RemoveRange(i - 5, 6);
					Plugin.Logger.LogDebug((object)"Transpiler (ClaySurgeonAI.OnDestroy): Don't remove listener from \"onHourChanged\"");
					i -= 5;
					list.Insert(i, new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(DanceClock), "Stop", (Type[])null, (Type[])null)));
					Plugin.Logger.LogDebug((object)"Transpiler (ClaySurgeonAI.OnDestroy): Use new \"DanceClock\"");
				}
			}
			return list;
		}

		[HarmonyPatch(typeof(ClaySurgeonAI), "KillPlayerClientRpc")]
		[HarmonyPostfix]
		private static void ClaySurgeonAIPostKillPlayerClientRpc(ClaySurgeonAI __instance, ref bool ___isMaster, ref float ___beatTimer, ref float ___snareIntervalTimer)
		{
			if (((NetworkBehaviour)__instance).IsOwner & ___isMaster)
			{
				___beatTimer = Mathf.Min(___beatTimer, 4f + __instance.snareOffset);
				___snareIntervalTimer = ___beatTimer - __instance.snareOffset;
			}
		}

		[HarmonyPatch(typeof(ClaySurgeonAI), "PlayMusic")]
		[HarmonyPostfix]
		private static void ClaySurgeonAIPostPlayMusic(ClaySurgeonAI __instance, float ___snareIntervalTimer)
		{
			if (___snareIntervalTimer != 100f || !Plugin.configDrumrollFromAll.Value)
			{
				return;
			}
			foreach (ClaySurgeonAI allClaySurgeon in __instance.allClaySurgeons)
			{
				if ((Object)(object)allClaySurgeon != (Object)(object)__instance)
				{
					allClaySurgeon.musicAudio.PlayOneShot(allClaySurgeon.snareDrum);
					WalkieTalkie.TransmitOneShotAudio(allClaySurgeon.musicAudio, allClaySurgeon.snareDrum, 1f);
				}
			}
		}

		[HarmonyPatch(typeof(RoundManager), "AdvanceHourAndSpawnNewBatchOfEnemies")]
		[HarmonyPrefix]
		private static void PreAdvanceHourAndSpawnNewBatchOfEnemies(RoundManager __instance)
		{
			if (!((NetworkBehaviour)__instance).IsServer || !Plugin.configApplySpawningSettings.Value)
			{
				return;
			}
			int num = 1;
			if (Plugin.configSpawnInPairs.Value)
			{
				if (Plugin.configOnlyOneBarber.Value)
				{
					Plugin.Logger.LogWarning((object)"Config setting \"SpawnInPairs\" has been enabled, but will be ignored as \"OnlyOneBarber\" is also enabled.");
				}
				else
				{
					num = 2;
					if (!Plugin.CAN_SPAWN_IN_GROUPS)
					{
						Plugin.Logger.LogWarning((object)"Config setting \"SpawnInPairs\" has been enabled, but VentSpawnFix was not detected. Enemies spawning from vents in groups is unsupported by vanilla, so this setting won't work!");
					}
				}
			}
			EnemyType val = ((IEnumerable<SpawnableEnemyWithRarity>)__instance.currentLevel.Enemies).FirstOrDefault((Func<SpawnableEnemyWithRarity, bool>)delegate(SpawnableEnemyWithRarity enemy)
			{
				EnemyType enemyType3 = enemy.enemyType;
				return ((enemyType3 != null) ? ((Object)enemyType3).name : null) == "ClaySurgeon";
			})?.enemyType;
			if ((Object)(object)val == (Object)null)
			{
				val = ((IEnumerable<SpawnableEnemyWithRarity>)__instance.currentLevel.OutsideEnemies).FirstOrDefault((Func<SpawnableEnemyWithRarity, bool>)delegate(SpawnableEnemyWithRarity enemy)
				{
					EnemyType enemyType2 = enemy.enemyType;
					return ((enemyType2 != null) ? ((Object)enemyType2).name : null) == "ClaySurgeon";
				})?.enemyType ?? ((IEnumerable<SpawnableEnemyWithRarity>)__instance.currentLevel.DaytimeEnemies).FirstOrDefault((Func<SpawnableEnemyWithRarity, bool>)delegate(SpawnableEnemyWithRarity enemy)
				{
					EnemyType enemyType = enemy.enemyType;
					return ((enemyType != null) ? ((Object)enemyType).name : null) == "ClaySurgeon";
				})?.enemyType;
				if ((Object)(object)val != (Object)null && !Plugin.CAN_SPAWN_IN_GROUPS)
				{
					Plugin.CAN_SPAWN_IN_GROUPS = true;
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				if (val.spawnInGroupsOf != num)
				{
					Plugin.Logger.LogDebug((object)$"ClaySurgeon.spawnInGroupsOf: {val.spawnInGroupsOf} -> {num}");
					val.spawnInGroupsOf = num;
				}
				int num2 = (Plugin.configOnlyOneBarber.Value ? 1 : 8);
				if (val.MaxCount != num2)
				{
					Plugin.Logger.LogDebug((object)$"ClaySurgeon.MaxCount: {val.MaxCount} -> {num2}");
					val.MaxCount = num2;
				}
			}
		}

		[HarmonyPatch(typeof(ClaySurgeonAI), "Start")]
		[HarmonyPostfix]
		private static void ClaySurgeonAIPostStart(ClaySurgeonAI __instance, ref float ___snareIntervalTimer)
		{
			((EnemyAI)__instance).agent.speed = 0f;
			___snareIntervalTimer = 100f;
			EnemyAICollisionDetect componentInChildren = ((Component)__instance).GetComponentInChildren<EnemyAICollisionDetect>();
			GameObject val = ((componentInChildren != null) ? ((Component)componentInChildren).gameObject : null);
			if ((Object)(object)val != (Object)null && !Object.op_Implicit((Object)(object)val.GetComponent<Rigidbody>()))
			{
				Rigidbody obj = val.AddComponent<Rigidbody>();
				obj.isKinematic = true;
				obj.collisionDetectionMode = (CollisionDetectionMode)3;
			}
		}

		[HarmonyPatch(typeof(ClaySurgeonAI), "OnCollideWithPlayer")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> ClaySurgeonAITransOnCollideWithPlayer(IEnumerable<CodeInstruction> instructions)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			List<CodeInstruction> list = instructions.ToList();
			list.InsertRange(list.Count - 1, new <>z__ReadOnlyArray<CodeInstruction>((CodeInstruction[])(object)new CodeInstruction[3]
			{
				new CodeInstruction(OpCodes.Ldarg_0, (object)null),
				new CodeInstruction(OpCodes.Ldc_R4, (object)0f),
				new CodeInstruction(OpCodes.Stfld, (object)AccessTools.Field(typeof(ClaySurgeonAI), "timeSinceSnip"))
			}));
			Plugin.Logger.LogDebug((object)"Transpiler (ClaySurgeonAI.OnCollideWithPlayer): Set cooldown for local client immediately");
			return list;
		}
	}
	public class DanceClock
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__Tick;
		}

		private static bool ticking;

		private static float startingInterval = 2.75f;

		private static float endingInterval = 1.25f;

		internal static void Start(float start, float end)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			if (!ticking)
			{
				ticking = true;
				startingInterval = start;
				endingInterval = end;
				UnityEvent onHourChanged = TimeOfDay.Instance.onHourChanged;
				object obj = <>O.<0>__Tick;
				if (obj == null)
				{
					UnityAction val = Tick;
					<>O.<0>__Tick = val;
					obj = (object)val;
				}
				onHourChanged.AddListener((UnityAction)obj);
			}
		}

		public static void Tick()
		{
			float currentInterval = Mathf.Lerp(startingInterval, endingInterval, (float)TimeOfDay.Instance.hour / (float)TimeOfDay.Instance.numberOfHours);
			ClaySurgeonAI[] array = Object.FindObjectsOfType<ClaySurgeonAI>();
			for (int i = 0; i < array.Length; i++)
			{
				array[i].currentInterval = currentInterval;
			}
		}

		internal static void Stop()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			if (ticking)
			{
				ticking = false;
				UnityEvent onHourChanged = TimeOfDay.Instance.onHourChanged;
				object obj = <>O.<0>__Tick;
				if (obj == null)
				{
					UnityAction val = Tick;
					<>O.<0>__Tick = val;
					obj = (object)val;
				}
				onHourChanged.RemoveListener((UnityAction)obj);
			}
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "BarberFixes";

		public const string PLUGIN_NAME = "BarberFixes";

		public const string PLUGIN_VERSION = "1.2.2";
	}
}
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();
	}
}