Decompiled source of BarberFixes v1.2.1

BarberFixes.dll

Decompiled a week 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.1.0")]
[assembly: AssemblyInformationalVersion("1.2.1+aa71829e449652ff57f84a134f7c9c0c545bdea7")]
[assembly: AssemblyProduct("BarberFixes")]
[assembly: AssemblyTitle("BarberFixes")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.1.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.1")]
	[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.1";

		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. LethalQuantities)");
			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.1 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), "PlotOutEnemiesForNextHour")]
		[HarmonyPrefix]
		private static void PrePlotOutEnemiesForNextHour(RoundManager __instance)
		{
			if (!((NetworkBehaviour)__instance).IsServer || !Plugin.configApplySpawningSettings.Value)
			{
				return;
			}
			int num = 1;
			if (Plugin.configSpawnInPairs.Value)
			{
				if (Plugin.CAN_SPAWN_IN_GROUPS)
				{
					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;
					}
				}
				else
				{
					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 enemyType = enemy.enemyType;
				return ((enemyType != null) ? ((Object)enemyType).name : null) == "ClaySurgeon";
			})?.enemyType;
			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.1";
	}
}
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();
	}
}